mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 12:53:11 +08:00
Merge branch 'master' into spectator-reliability
This commit is contained in:
commit
e947c97e10
@ -18,7 +18,6 @@
|
|||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0" PrivateAssets="All" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.217.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.223.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -77,10 +77,9 @@ namespace osu.Desktop.LegacyIpc
|
|||||||
case LegacyIpcDifficultyCalculationRequest req:
|
case LegacyIpcDifficultyCalculationRequest req:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var ruleset = getLegacyRulesetFromID(req.RulesetId);
|
WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile);
|
||||||
|
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||||
Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();
|
Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();
|
||||||
WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile, _ => ruleset);
|
|
||||||
|
|
||||||
return new LegacyIpcDifficultyCalculationResponse
|
return new LegacyIpcDifficultyCalculationResponse
|
||||||
{
|
{
|
||||||
|
@ -8,12 +8,15 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
@ -23,13 +26,37 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
protected override bool AllowFail => true;
|
protected override bool AllowFail => true;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSpinnerAutoCompleted() => CreateModTest(new ModTestData
|
public void TestSpinnerAutoCompleted()
|
||||||
|
{
|
||||||
|
DrawableSpinner spinner = null;
|
||||||
|
JudgementResult lastResult = null;
|
||||||
|
|
||||||
|
CreateModTest(new ModTestData
|
||||||
{
|
{
|
||||||
Mod = new OsuModSpunOut(),
|
Mod = new OsuModSpunOut(),
|
||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = singleSpinnerBeatmap,
|
Beatmap = singleSpinnerBeatmap,
|
||||||
PassCondition = () => Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault()?.Progress >= 1
|
PassCondition = () =>
|
||||||
|
{
|
||||||
|
// Bind to the first spinner's results for further tracking.
|
||||||
|
if (spinner == null)
|
||||||
|
{
|
||||||
|
// We only care about the first spinner we encounter for this test.
|
||||||
|
var nextSpinner = Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault();
|
||||||
|
|
||||||
|
if (nextSpinner == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lastResult = null;
|
||||||
|
|
||||||
|
spinner = nextSpinner;
|
||||||
|
spinner.OnNewResult += (o, result) => lastResult = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastResult?.Type == HitResult.Great;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(null)]
|
[TestCase(null)]
|
||||||
[TestCase(typeof(OsuModDoubleTime))]
|
[TestCase(typeof(OsuModDoubleTime))]
|
||||||
@ -48,7 +75,57 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
PassCondition = () =>
|
PassCondition = () =>
|
||||||
{
|
{
|
||||||
var counter = Player.ChildrenOfType<SpinnerSpmCalculator>().SingleOrDefault();
|
var counter = Player.ChildrenOfType<SpinnerSpmCalculator>().SingleOrDefault();
|
||||||
return counter != null && Precision.AlmostEquals(counter.Result.Value, 286, 1);
|
var spinner = Player.ChildrenOfType<DrawableSpinner>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (counter == null || spinner == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ignore cases where the spinner hasn't started as these lead to false-positives
|
||||||
|
if (Precision.AlmostEquals(counter.Result.Value, 0, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float rotationSpeed = (float)(1.01 * spinner.HitObject.SpinsRequired / spinner.HitObject.Duration);
|
||||||
|
|
||||||
|
return Precision.AlmostEquals(counter.Result.Value, rotationSpeed * 1000 * 60, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSpinnerGetsNoBonusScore()
|
||||||
|
{
|
||||||
|
DrawableSpinner spinner = null;
|
||||||
|
List<JudgementResult> results = new List<JudgementResult>();
|
||||||
|
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModSpunOut(),
|
||||||
|
Autoplay = false,
|
||||||
|
Beatmap = singleSpinnerBeatmap,
|
||||||
|
PassCondition = () =>
|
||||||
|
{
|
||||||
|
// Bind to the first spinner's results for further tracking.
|
||||||
|
if (spinner == null)
|
||||||
|
{
|
||||||
|
// We only care about the first spinner we encounter for this test.
|
||||||
|
var nextSpinner = Player.ChildrenOfType<DrawableSpinner>().SingleOrDefault();
|
||||||
|
|
||||||
|
if (nextSpinner == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
spinner = nextSpinner;
|
||||||
|
spinner.OnNewResult += (o, result) => results.Add(result);
|
||||||
|
|
||||||
|
results.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should only be checking the bonus/progress after the spinner has fully completed.
|
||||||
|
if (results.OfType<OsuSpinnerJudgementResult>().All(r => r.TimeCompleted == null))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return
|
||||||
|
results.Any(r => r.Type == HitResult.SmallBonus)
|
||||||
|
&& results.All(r => r.Type != HitResult.LargeBonus);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
// for that reason using ElapsedFrameTime directly leads to fewer SPM with Half Time and more SPM with Double Time.
|
// for that reason using ElapsedFrameTime directly leads to fewer SPM with Half Time and more SPM with Double Time.
|
||||||
// for spinners we want the real (wall clock) elapsed time; to achieve that, unapply the clock rate locally here.
|
// for spinners we want the real (wall clock) elapsed time; to achieve that, unapply the clock rate locally here.
|
||||||
double rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate;
|
double rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate;
|
||||||
spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * 0.03f));
|
|
||||||
|
// multiply the SPM by 1.01 to ensure that the spinner is completed. if the calculation is left exact,
|
||||||
|
// some spinners may not complete due to very minor decimal loss during calculation
|
||||||
|
float rotationSpeed = (float)(1.01 * spinner.HitObject.SpinsRequired / spinner.HitObject.Duration);
|
||||||
|
spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * rotationSpeed * MathF.PI * 2.0f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,26 +409,26 @@ namespace osu.Game.Tests.Chat
|
|||||||
|
|
||||||
Assert.AreEqual(result.Content, result.DisplayContent);
|
Assert.AreEqual(result.Content, result.DisplayContent);
|
||||||
Assert.AreEqual(2, result.Links.Count);
|
Assert.AreEqual(2, result.Links.Count);
|
||||||
Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
|
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
|
||||||
Assert.AreEqual("osu://chan/#japanese", result.Links[1].Url);
|
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#japanese", result.Links[1].Url);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestOsuProtocol()
|
public void TestOsuProtocol()
|
||||||
{
|
{
|
||||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a custom protocol osu://chan/#english." });
|
Message result = MessageFormatter.FormatMessage(new Message { Content = $"This is a custom protocol {OsuGameBase.OSU_PROTOCOL}chan/#english." });
|
||||||
|
|
||||||
Assert.AreEqual(result.Content, result.DisplayContent);
|
Assert.AreEqual(result.Content, result.DisplayContent);
|
||||||
Assert.AreEqual(1, result.Links.Count);
|
Assert.AreEqual(1, result.Links.Count);
|
||||||
Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
|
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
|
||||||
Assert.AreEqual(26, result.Links[0].Index);
|
Assert.AreEqual(26, result.Links[0].Index);
|
||||||
Assert.AreEqual(19, result.Links[0].Length);
|
Assert.AreEqual(19, result.Links[0].Length);
|
||||||
|
|
||||||
result = MessageFormatter.FormatMessage(new Message { Content = "This is a [custom protocol](osu://chan/#english)." });
|
result = MessageFormatter.FormatMessage(new Message { Content = $"This is a [custom protocol]({OsuGameBase.OSU_PROTOCOL}chan/#english)." });
|
||||||
|
|
||||||
Assert.AreEqual("This is a custom protocol.", result.DisplayContent);
|
Assert.AreEqual("This is a custom protocol.", result.DisplayContent);
|
||||||
Assert.AreEqual(1, result.Links.Count);
|
Assert.AreEqual(1, result.Links.Count);
|
||||||
Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
|
Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
|
||||||
Assert.AreEqual("#english", result.Links[0].Argument);
|
Assert.AreEqual("#english", result.Links[0].Argument);
|
||||||
Assert.AreEqual(10, result.Links[0].Index);
|
Assert.AreEqual(10, result.Links[0].Index);
|
||||||
Assert.AreEqual(15, result.Links[0].Length);
|
Assert.AreEqual(15, result.Links[0].Length);
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using (var importer = new BeatmapModelManager(realm, storage))
|
using (var importer = new BeatmapModelManager(realm, storage))
|
||||||
using (new RulesetStore(realm, storage))
|
using (new RealmRulesetStore(realm, storage))
|
||||||
{
|
{
|
||||||
Live<BeatmapSetInfo>? beatmapSet;
|
Live<BeatmapSetInfo>? beatmapSet;
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using (var importer = new BeatmapModelManager(realm, storage))
|
using (var importer = new BeatmapModelManager(realm, storage))
|
||||||
using (new RulesetStore(realm, storage))
|
using (new RealmRulesetStore(realm, storage))
|
||||||
{
|
{
|
||||||
Live<BeatmapSetInfo>? beatmapSet;
|
Live<BeatmapSetInfo>? beatmapSet;
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using (var importer = new BeatmapModelManager(realm, storage))
|
using (var importer = new BeatmapModelManager(realm, storage))
|
||||||
using (new RulesetStore(realm, storage))
|
using (new RealmRulesetStore(realm, storage))
|
||||||
{
|
{
|
||||||
Live<BeatmapSetInfo>? imported;
|
Live<BeatmapSetInfo>? imported;
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
await LoadOszIntoStore(importer, realm.Realm);
|
await LoadOszIntoStore(importer, realm.Realm);
|
||||||
});
|
});
|
||||||
@ -183,7 +183,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? tempPath = TestResources.GetTestBeatmapForImport();
|
string? tempPath = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
var importedSecondTime = await LoadOszIntoStore(importer, realm.Realm);
|
var importedSecondTime = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
@ -265,7 +265,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -414,7 +414,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -496,7 +496,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var progressNotification = new ImportProgressNotification();
|
var progressNotification = new ImportProgressNotification();
|
||||||
|
|
||||||
@ -532,7 +532,7 @@ namespace osu.Game.Tests.Database
|
|||||||
};
|
};
|
||||||
|
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -582,7 +582,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -606,7 +606,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realmFactory, storage) =>
|
RunTestWithRealmAsync(async (realmFactory, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realmFactory, storage);
|
using var importer = new BeatmapModelManager(realmFactory, storage);
|
||||||
using var store = new RulesetStore(realmFactory, storage);
|
using var store = new RealmRulesetStore(realmFactory, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realmFactory.Realm);
|
var imported = await LoadOszIntoStore(importer, realmFactory.Realm);
|
||||||
|
|
||||||
@ -638,7 +638,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new NonOptimisedBeatmapImporter(realm, storage);
|
using var importer = new NonOptimisedBeatmapImporter(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -662,7 +662,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
@ -688,7 +688,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealm((realm, storage) =>
|
RunTestWithRealm((realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
var metadata = new BeatmapMetadata
|
var metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
@ -734,7 +734,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
using (File.OpenRead(temp))
|
using (File.OpenRead(temp))
|
||||||
@ -751,7 +751,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -787,7 +787,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -829,7 +829,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -880,7 +880,7 @@ namespace osu.Game.Tests.Database
|
|||||||
RunTestWithRealmAsync(async (realm, storage) =>
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
{
|
{
|
||||||
using var importer = new BeatmapModelManager(realm, storage);
|
using var importer = new BeatmapModelManager(realm, storage);
|
||||||
using var store = new RulesetStore(realm, storage);
|
using var store = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
string? temp = TestResources.GetTestBeatmapForImport();
|
string? temp = TestResources.GetTestBeatmapForImport();
|
||||||
await importer.Import(temp);
|
await importer.Import(temp);
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tests.Database
|
|||||||
{
|
{
|
||||||
RunTestWithRealm((realm, storage) =>
|
RunTestWithRealm((realm, storage) =>
|
||||||
{
|
{
|
||||||
var rulesets = new RulesetStore(realm, storage);
|
var rulesets = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
Assert.AreEqual(4, rulesets.AvailableRulesets.Count());
|
Assert.AreEqual(4, rulesets.AvailableRulesets.Count());
|
||||||
Assert.AreEqual(4, realm.Realm.All<RulesetInfo>().Count());
|
Assert.AreEqual(4, realm.Realm.All<RulesetInfo>().Count());
|
||||||
@ -26,8 +26,8 @@ namespace osu.Game.Tests.Database
|
|||||||
{
|
{
|
||||||
RunTestWithRealm((realm, storage) =>
|
RunTestWithRealm((realm, storage) =>
|
||||||
{
|
{
|
||||||
var rulesets = new RulesetStore(realm, storage);
|
var rulesets = new RealmRulesetStore(realm, storage);
|
||||||
var rulesets2 = new RulesetStore(realm, storage);
|
var rulesets2 = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
Assert.AreEqual(4, rulesets.AvailableRulesets.Count());
|
Assert.AreEqual(4, rulesets.AvailableRulesets.Count());
|
||||||
Assert.AreEqual(4, rulesets2.AvailableRulesets.Count());
|
Assert.AreEqual(4, rulesets2.AvailableRulesets.Count());
|
||||||
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Database
|
|||||||
{
|
{
|
||||||
RunTestWithRealm((realm, storage) =>
|
RunTestWithRealm((realm, storage) =>
|
||||||
{
|
{
|
||||||
var rulesets = new RulesetStore(realm, storage);
|
var rulesets = new RealmRulesetStore(realm, storage);
|
||||||
|
|
||||||
Assert.IsFalse(rulesets.AvailableRulesets.First().IsManaged);
|
Assert.IsFalse(rulesets.AvailableRulesets.First().IsManaged);
|
||||||
Assert.IsFalse(rulesets.GetRuleset(0)?.IsManaged);
|
Assert.IsFalse(rulesets.GetRuleset(0)?.IsManaged);
|
||||||
|
@ -121,6 +121,17 @@ namespace osu.Game.Tests.Online
|
|||||||
Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2));
|
Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAPIModDetachedFromSource()
|
||||||
|
{
|
||||||
|
var mod = new OsuModDoubleTime { SpeedChange = { Value = 1.01 } };
|
||||||
|
var apiMod = new APIMod(mod);
|
||||||
|
|
||||||
|
mod.SpeedChange.Value = 1.5;
|
||||||
|
|
||||||
|
Assert.That(apiMod.Settings["speed_change"], Is.EqualTo(1.01d));
|
||||||
|
}
|
||||||
|
|
||||||
private class TestRuleset : Ruleset
|
private class TestRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Online
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio, GameHost host)
|
private void load(AudioManager audio, GameHost host)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestBeatmapModelDownloader(beatmaps, API));
|
Dependencies.CacheAs<BeatmapModelDownloader>(beatmapDownloader = new TestBeatmapModelDownloader(beatmaps, API));
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Collections
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host)
|
private void load(GameHost host)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, Audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, Audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -377,7 +378,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap);
|
Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap);
|
||||||
Ruleset.Value = importedBeatmap.Ruleset;
|
Ruleset.Value = importedBeatmap.Ruleset;
|
||||||
|
|
||||||
LoadScreen(spectatorScreen = new TestMultiSpectatorScreen(playingUsers.ToArray(), gameplayStartTime));
|
LoadScreen(spectatorScreen = new TestMultiSpectatorScreen(SelectedRoom.Value, playingUsers.ToArray(), gameplayStartTime));
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
|
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
|
||||||
@ -465,8 +466,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
private readonly double? gameplayStartTime;
|
private readonly double? gameplayStartTime;
|
||||||
|
|
||||||
public TestMultiSpectatorScreen(MultiplayerRoomUser[] users, double? gameplayStartTime = null)
|
public TestMultiSpectatorScreen(Room room, MultiplayerRoomUser[] users, double? gameplayStartTime = null)
|
||||||
: base(users)
|
: base(room, users)
|
||||||
{
|
{
|
||||||
this.gameplayStartTime = gameplayStartTime;
|
this.gameplayStartTime = gameplayStartTime;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Navigation
|
||||||
|
{
|
||||||
|
public class TestSceneStartupBeatmapDisplay : OsuGameTestScene
|
||||||
|
{
|
||||||
|
private const int requested_beatmap_id = 75;
|
||||||
|
private const int requested_beatmap_set_id = 1;
|
||||||
|
|
||||||
|
protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { $"osu://b/{requested_beatmap_id}" });
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
((DummyAPIAccess)API).HandleRequest = request =>
|
||||||
|
{
|
||||||
|
switch (request)
|
||||||
|
{
|
||||||
|
case GetBeatmapSetRequest gbr:
|
||||||
|
var apiBeatmapSet = CreateAPIBeatmapSet();
|
||||||
|
apiBeatmapSet.OnlineID = requested_beatmap_set_id;
|
||||||
|
apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
|
||||||
|
{
|
||||||
|
DifficultyName = "Target difficulty",
|
||||||
|
OnlineID = requested_beatmap_id,
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
gbr.TriggerSuccess(apiBeatmapSet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapLink()
|
||||||
|
{
|
||||||
|
AddUntilStep("Beatmap overlay displayed", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||||
|
AddUntilStep("Beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapPicker>().FirstOrDefault()?.Beatmap.Value.OnlineID == requested_beatmap_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Navigation
|
||||||
|
{
|
||||||
|
public class TestSceneStartupBeatmapSetDisplay : OsuGameTestScene
|
||||||
|
{
|
||||||
|
private const int requested_beatmap_set_id = 1;
|
||||||
|
|
||||||
|
protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { $"osu://s/{requested_beatmap_set_id}" });
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
((DummyAPIAccess)API).HandleRequest = request =>
|
||||||
|
{
|
||||||
|
switch (request)
|
||||||
|
{
|
||||||
|
case GetBeatmapSetRequest gbr:
|
||||||
|
|
||||||
|
var apiBeatmapSet = CreateAPIBeatmapSet();
|
||||||
|
apiBeatmapSet.OnlineID = requested_beatmap_set_id;
|
||||||
|
apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
|
||||||
|
{
|
||||||
|
DifficultyName = "Target difficulty",
|
||||||
|
OnlineID = 75,
|
||||||
|
}).ToArray();
|
||||||
|
gbr.TriggerSuccess(apiBeatmapSet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapSetLink()
|
||||||
|
{
|
||||||
|
AddUntilStep("Beatmap overlay displayed", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||||
|
AddUntilStep("Beatmap overlay showing content", () => Game.ChildrenOfType<BeatmapSetOverlay>().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -87,8 +87,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External);
|
addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External);
|
||||||
addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
|
addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
|
||||||
addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
|
addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
|
||||||
addMessageWithChecks("Join my [#english](osu://chan/#english).", 1, expectedActions: LinkAction.OpenChannel);
|
addMessageWithChecks($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", 1, expectedActions: LinkAction.OpenChannel);
|
||||||
addMessageWithChecks("Join my osu://chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
|
addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
|
||||||
addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel });
|
addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel });
|
||||||
addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel);
|
addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel);
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, API, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
dependencies.Cache(rulesetStore = new RulesetStore(Realm));
|
dependencies.Cache(rulesetStore = new RealmRulesetStore(Realm));
|
||||||
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
||||||
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, Scheduler));
|
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, Realm, Scheduler));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host)
|
private void load(GameHost host)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, Audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, Audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
// These DI caches are required to ensure for interactive runs this test scene doesn't nuke all user beatmaps in the local install.
|
// These DI caches are required to ensure for interactive runs this test scene doesn't nuke all user beatmaps in the local install.
|
||||||
// At a point we have isolated interactive test runs enough, this can likely be removed.
|
// At a point we have isolated interactive test runs enough, this can likely be removed.
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, defaultBeatmap = Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, defaultBeatmap = Beatmap.Default));
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(GameHost host, AudioManager audio)
|
private void load(GameHost host, AudioManager audio)
|
||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(Realm));
|
Dependencies.Cache(rulesets = new RealmRulesetStore(Realm));
|
||||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||||
Dependencies.Cache(scoreManager = new ScoreManager(rulesets, () => beatmapManager, LocalStorage, Realm, Scheduler));
|
Dependencies.Cache(scoreManager = new ScoreManager(rulesets, () => beatmapManager, LocalStorage, Realm, Scheduler));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
dependencies.Cache(rulesetStore = new RulesetStore(Realm));
|
dependencies.Cache(rulesetStore = new RealmRulesetStore(Realm));
|
||||||
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, Realm, rulesetStore, null, dependencies.Get<AudioManager>(), Resources, dependencies.Get<GameHost>(), Beatmap.Default));
|
||||||
dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get<RulesetStore>(), () => beatmapManager, LocalStorage, Realm, Scheduler));
|
dependencies.Cache(scoreManager = new ScoreManager(dependencies.Get<RulesetStore>(), () => beatmapManager, LocalStorage, Realm, Scheduler));
|
||||||
Dependencies.Cache(Realm);
|
Dependencies.Cache(Realm);
|
||||||
|
81
osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs
Normal file
81
osu.Game.Tests/Visual/UserInterface/TestSceneModPanel.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneModPanel : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVariousPanels()
|
||||||
|
{
|
||||||
|
AddStep("create content", () => Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Width = 300,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(0, 5),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new ModPanel(new OsuModHalfTime()),
|
||||||
|
new ModPanel(new OsuModFlashlight()),
|
||||||
|
new ModPanel(new OsuModAutoplay()),
|
||||||
|
new ModPanel(new OsuModAlternate()),
|
||||||
|
new ModPanel(new OsuModApproachDifferent())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIncompatibilityDisplay()
|
||||||
|
{
|
||||||
|
IncompatibilityDisplayingModPanel panel = null;
|
||||||
|
|
||||||
|
AddStep("create panel with DT", () => Child = panel = new IncompatibilityDisplayingModPanel(new OsuModDoubleTime())
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Width = 300
|
||||||
|
});
|
||||||
|
|
||||||
|
clickPanel();
|
||||||
|
AddAssert("panel active", () => panel.Active.Value);
|
||||||
|
|
||||||
|
clickPanel();
|
||||||
|
AddAssert("panel not active", () => !panel.Active.Value);
|
||||||
|
|
||||||
|
AddStep("set incompatible mod", () => SelectedMods.Value = new[] { new OsuModHalfTime() });
|
||||||
|
|
||||||
|
clickPanel();
|
||||||
|
AddAssert("panel not active", () => !panel.Active.Value);
|
||||||
|
|
||||||
|
AddStep("reset mods", () => SelectedMods.Value = Array.Empty<Mod>());
|
||||||
|
|
||||||
|
clickPanel();
|
||||||
|
AddAssert("panel active", () => panel.Active.Value);
|
||||||
|
|
||||||
|
void clickPanel() => AddStep("click panel", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(panel);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneModSwitchSmall : OsuTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestOsu() => createSwitchTestFor(new OsuRuleset());
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTaiko() => createSwitchTestFor(new TaikoRuleset());
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCatch() => createSwitchTestFor(new CatchRuleset());
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMania() => createSwitchTestFor(new ManiaRuleset());
|
||||||
|
|
||||||
|
private void createSwitchTestFor(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
AddStep("no colour scheme", () => Child = createContent(ruleset, null));
|
||||||
|
|
||||||
|
foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast<OverlayColourScheme>())
|
||||||
|
{
|
||||||
|
AddStep($"{scheme} colour scheme", () => Child = createContent(ruleset, scheme));
|
||||||
|
}
|
||||||
|
|
||||||
|
AddToggleStep("toggle active", active => this.ChildrenOfType<ModSwitchTiny>().ForEach(s => s.Active.Value = active));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Drawable createContent(Ruleset ruleset, OverlayColourScheme? colourScheme)
|
||||||
|
{
|
||||||
|
var switchFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
Padding = new MarginPadding(20),
|
||||||
|
ChildrenEnumerable = ruleset.CreateAllMods()
|
||||||
|
.GroupBy(mod => mod.Type)
|
||||||
|
.Select(group => new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Full,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
ChildrenEnumerable = group.Select(mod => new ModSwitchSmall(mod))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if (colourScheme != null)
|
||||||
|
{
|
||||||
|
return new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new (Type, object)[]
|
||||||
|
{
|
||||||
|
(typeof(OverlayColourProvider), new OverlayColourProvider(colourScheme.Value))
|
||||||
|
},
|
||||||
|
Child = switchFlow
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return switchFlow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneModSwitchTiny : OsuTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestOsu() => createSwitchTestFor(new OsuRuleset());
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTaiko() => createSwitchTestFor(new TaikoRuleset());
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCatch() => createSwitchTestFor(new CatchRuleset());
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMania() => createSwitchTestFor(new ManiaRuleset());
|
||||||
|
|
||||||
|
private void createSwitchTestFor(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
AddStep("no colour scheme", () => Child = createContent(ruleset, null));
|
||||||
|
|
||||||
|
foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast<OverlayColourScheme>())
|
||||||
|
{
|
||||||
|
AddStep($"{scheme} colour scheme", () => Child = createContent(ruleset, scheme));
|
||||||
|
}
|
||||||
|
|
||||||
|
AddToggleStep("toggle active", active => this.ChildrenOfType<ModSwitchTiny>().ForEach(s => s.Active.Value = active));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Drawable createContent(Ruleset ruleset, OverlayColourScheme? colourScheme)
|
||||||
|
{
|
||||||
|
var switchFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
Padding = new MarginPadding(20),
|
||||||
|
ChildrenEnumerable = ruleset.CreateAllMods()
|
||||||
|
.GroupBy(mod => mod.Type)
|
||||||
|
.Select(group => new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Full,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
ChildrenEnumerable = group.Select(mod => new ModSwitchTiny(mod))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if (colourScheme != null)
|
||||||
|
{
|
||||||
|
return new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new (Type, object)[]
|
||||||
|
{
|
||||||
|
(typeof(OverlayColourProvider), new OverlayColourProvider(colourScheme.Value))
|
||||||
|
},
|
||||||
|
Child = switchFlow
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return switchFlow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -152,18 +152,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
#region Compatibility properties
|
#region Compatibility properties
|
||||||
|
|
||||||
[Ignored]
|
|
||||||
public int RulesetID
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(Ruleset.InstantiationInfo))
|
|
||||||
throw new InvalidOperationException($"Cannot set a {nameof(RulesetID)} when {nameof(Ruleset)} is already set to an actual ruleset.");
|
|
||||||
|
|
||||||
Ruleset.OnlineID = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Ignored]
|
[Ignored]
|
||||||
[Obsolete("Use BeatmapInfo.Difficulty instead.")] // can be removed 20220719
|
[Obsolete("Use BeatmapInfo.Difficulty instead.")] // can be removed 20220719
|
||||||
public BeatmapDifficulty BaseDifficulty
|
public BeatmapDifficulty BaseDifficulty
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Audio.Track;
|
|||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@ -20,18 +19,16 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
private readonly Beatmap beatmap;
|
private readonly Beatmap beatmap;
|
||||||
|
|
||||||
public FlatFileWorkingBeatmap(string file, Func<int, Ruleset> rulesetProvider, int? beatmapId = null)
|
public FlatFileWorkingBeatmap(string file, int? beatmapId = null)
|
||||||
: this(readFromFile(file), rulesetProvider, beatmapId)
|
: this(readFromFile(file), beatmapId)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private FlatFileWorkingBeatmap(Beatmap beatmap, Func<int, Ruleset> rulesetProvider, int? beatmapId = null)
|
private FlatFileWorkingBeatmap(Beatmap beatmap, int? beatmapId = null)
|
||||||
: base(beatmap.BeatmapInfo, null)
|
: base(beatmap.BeatmapInfo, null)
|
||||||
{
|
{
|
||||||
this.beatmap = beatmap;
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
beatmap.BeatmapInfo.Ruleset = rulesetProvider(beatmap.BeatmapInfo.Ruleset.OnlineID).RulesetInfo;
|
|
||||||
|
|
||||||
if (beatmapId.HasValue)
|
if (beatmapId.HasValue)
|
||||||
beatmap.BeatmapInfo.OnlineID = beatmapId.Value;
|
beatmap.BeatmapInfo.OnlineID = beatmapId.Value;
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
@ -37,6 +39,15 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
LegacyStoryboardDecoder.Register();
|
LegacyStoryboardDecoder.Register();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register dependencies for use with static decoder classes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rulesets">A store containing all available rulesets (used by <see cref="LegacyBeatmapDecoder"/>).</param>
|
||||||
|
public static void RegisterDependencies([NotNull] RulesetStore rulesets)
|
||||||
|
{
|
||||||
|
LegacyBeatmapDecoder.RulesetStore = rulesets ?? throw new ArgumentNullException(nameof(rulesets));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Beatmap"/>.
|
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Beatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,16 +7,20 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
public class LegacyBeatmapDecoder : LegacyDecoder<Beatmap>
|
public class LegacyBeatmapDecoder : LegacyDecoder<Beatmap>
|
||||||
{
|
{
|
||||||
|
internal static RulesetStore RulesetStore;
|
||||||
|
|
||||||
private Beatmap beatmap;
|
private Beatmap beatmap;
|
||||||
|
|
||||||
private ConvertHitObjectParser parser;
|
private ConvertHitObjectParser parser;
|
||||||
@ -40,6 +44,12 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
public LegacyBeatmapDecoder(int version = LATEST_VERSION)
|
public LegacyBeatmapDecoder(int version = LATEST_VERSION)
|
||||||
: base(version)
|
: base(version)
|
||||||
{
|
{
|
||||||
|
if (RulesetStore == null)
|
||||||
|
{
|
||||||
|
Logger.Log($"A {nameof(RulesetStore)} was not provided via {nameof(Decoder)}.{nameof(RegisterDependencies)}; falling back to default {nameof(AssemblyRulesetStore)}.");
|
||||||
|
RulesetStore = new AssemblyRulesetStore();
|
||||||
|
}
|
||||||
|
|
||||||
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
|
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
|
||||||
offset = FormatVersion < 5 ? 24 : 0;
|
offset = FormatVersion < 5 ? 24 : 0;
|
||||||
}
|
}
|
||||||
@ -158,7 +168,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
case @"Mode":
|
case @"Mode":
|
||||||
int rulesetID = Parsing.ParseInt(pair.Value);
|
int rulesetID = Parsing.ParseInt(pair.Value);
|
||||||
|
|
||||||
beatmap.BeatmapInfo.RulesetID = rulesetID;
|
beatmap.BeatmapInfo.Ruleset = RulesetStore.GetRuleset(rulesetID) ?? throw new ArgumentException("Ruleset is not available locally.");
|
||||||
|
|
||||||
switch (rulesetID)
|
switch (rulesetID)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
@ -157,6 +158,36 @@ namespace osu.Game.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the main accent colour for a <see cref="ModType"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Color4 ForModType(ModType modType)
|
||||||
|
{
|
||||||
|
switch (modType)
|
||||||
|
{
|
||||||
|
case ModType.Automation:
|
||||||
|
return Blue1;
|
||||||
|
|
||||||
|
case ModType.DifficultyIncrease:
|
||||||
|
return Red1;
|
||||||
|
|
||||||
|
case ModType.DifficultyReduction:
|
||||||
|
return Lime1;
|
||||||
|
|
||||||
|
case ModType.Conversion:
|
||||||
|
return Purple1;
|
||||||
|
|
||||||
|
case ModType.Fun:
|
||||||
|
return Pink1;
|
||||||
|
|
||||||
|
case ModType.System:
|
||||||
|
return Gray7;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(modType), modType, "Unknown mod type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a foreground text colour that is supposed to contrast well with
|
/// Returns a foreground text colour that is supposed to contrast well with
|
||||||
/// the supplied <paramref name="backgroundColour"/>.
|
/// the supplied <paramref name="backgroundColour"/>.
|
||||||
|
@ -36,6 +36,8 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public string WebsiteRootUrl { get; }
|
public string WebsiteRootUrl { get; }
|
||||||
|
|
||||||
|
public int APIVersion => 20220217; // We may want to pull this from the game version eventually.
|
||||||
|
|
||||||
public Exception LastLoginError { get; private set; }
|
public Exception LastLoginError { get; private set; }
|
||||||
|
|
||||||
public string ProvidedUsername { get; private set; }
|
public string ProvidedUsername { get; private set; }
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Online.API
|
|||||||
var bindable = (IBindable)property.GetValue(mod);
|
var bindable = (IBindable)property.GetValue(mod);
|
||||||
|
|
||||||
if (!bindable.IsDefault)
|
if (!bindable.IsDefault)
|
||||||
Settings.Add(property.Name.Underscore(), bindable);
|
Settings.Add(property.Name.Underscore(), ModUtils.GetSettingUnderlyingValue(bindable));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.IO.Network;
|
using osu.Framework.IO.Network;
|
||||||
@ -112,6 +113,9 @@ namespace osu.Game.Online.API
|
|||||||
WebRequest = CreateWebRequest();
|
WebRequest = CreateWebRequest();
|
||||||
WebRequest.Failed += Fail;
|
WebRequest.Failed += Fail;
|
||||||
WebRequest.AllowRetryOnTimeout = false;
|
WebRequest.AllowRetryOnTimeout = false;
|
||||||
|
|
||||||
|
WebRequest.AddHeader("x-api-version", API.APIVersion.ToString(CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(API.AccessToken))
|
if (!string.IsNullOrEmpty(API.AccessToken))
|
||||||
WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}");
|
WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}");
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public string WebsiteRootUrl => "http://localhost";
|
public string WebsiteRootUrl => "http://localhost";
|
||||||
|
|
||||||
|
public int APIVersion => int.Parse(DateTime.Now.ToString("yyyyMMdd"));
|
||||||
|
|
||||||
public Exception LastLoginError { get; private set; }
|
public Exception LastLoginError { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -57,6 +57,11 @@ namespace osu.Game.Online.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string WebsiteRootUrl { get; }
|
string WebsiteRootUrl { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The version of the API.
|
||||||
|
/// </summary>
|
||||||
|
int APIVersion { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last login error that occurred, if any.
|
/// The last login error that occurred, if any.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests.Responses
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
{
|
{
|
||||||
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public class APIUser : IEquatable<APIUser>, IUser
|
public class APIUser : IEquatable<APIUser>, IUser
|
||||||
{
|
{
|
||||||
[JsonProperty(@"id")]
|
[JsonProperty(@"id")]
|
||||||
|
@ -618,7 +618,7 @@ namespace osu.Game.Online.Chat
|
|||||||
var req = new MarkChannelAsReadRequest(channel, message);
|
var req = new MarkChannelAsReadRequest(channel, message);
|
||||||
|
|
||||||
req.Success += () => channel.LastReadId = message.Id;
|
req.Success += () => channel.LastReadId = message.Id;
|
||||||
req.Failure += e => Logger.Error(e, $"Failed to mark channel {channel} up to '{message}' as read");
|
req.Failure += e => Logger.Log($"Failed to mark channel {channel} up to '{message}' as read ({e.Message})", LoggingTarget.Network);
|
||||||
|
|
||||||
api.Queue(req);
|
api.Queue(req);
|
||||||
}
|
}
|
||||||
|
@ -268,10 +268,10 @@ namespace osu.Game.Online.Chat
|
|||||||
handleAdvanced(advanced_link_regex, result, startIndex);
|
handleAdvanced(advanced_link_regex, result, startIndex);
|
||||||
|
|
||||||
// handle editor times
|
// handle editor times
|
||||||
handleMatches(time_regex, "{0}", "osu://edit/{0}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
handleMatches(time_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
||||||
|
|
||||||
// handle channels
|
// handle channels
|
||||||
handleMatches(channel_regex, "{0}", "osu://chan/{0}", result, startIndex, LinkAction.OpenChannel);
|
handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
|
||||||
|
|
||||||
string empty = "";
|
string empty = "";
|
||||||
while (space-- > 0)
|
while (space-- > 0)
|
||||||
|
@ -62,6 +62,10 @@ namespace osu.Game.Online.Rooms
|
|||||||
[JsonProperty("beatmap_id")]
|
[JsonProperty("beatmap_id")]
|
||||||
private int onlineBeatmapId => Beatmap.OnlineID;
|
private int onlineBeatmapId => Beatmap.OnlineID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A beatmap representing this playlist item.
|
||||||
|
/// In many cases, this will *not* contain any usable information apart from OnlineID.
|
||||||
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IBeatmapInfo Beatmap { get; set; } = null!;
|
public IBeatmapInfo Beatmap { get; set; } = null!;
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -15,6 +14,7 @@ using osu.Game.Utils;
|
|||||||
|
|
||||||
namespace osu.Game.Online.Rooms
|
namespace osu.Game.Online.Rooms
|
||||||
{
|
{
|
||||||
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public class Room : IDeepCloneable<Room>
|
public class Room : IDeepCloneable<Room>
|
||||||
{
|
{
|
||||||
[Cached]
|
[Cached]
|
||||||
@ -37,8 +37,19 @@ namespace osu.Game.Online.Rooms
|
|||||||
[JsonProperty("channel_id")]
|
[JsonProperty("channel_id")]
|
||||||
public readonly Bindable<int> ChannelId = new Bindable<int>();
|
public readonly Bindable<int> ChannelId = new Bindable<int>();
|
||||||
|
|
||||||
|
[JsonProperty("current_playlist_item")]
|
||||||
|
[Cached]
|
||||||
|
public readonly Bindable<PlaylistItem> CurrentPlaylistItem = new Bindable<PlaylistItem>();
|
||||||
|
|
||||||
|
[JsonProperty("playlist_item_stats")]
|
||||||
|
[Cached]
|
||||||
|
public readonly Bindable<RoomPlaylistItemStats> PlaylistItemStats = new Bindable<RoomPlaylistItemStats>();
|
||||||
|
|
||||||
|
[JsonProperty("difficulty_range")]
|
||||||
|
[Cached]
|
||||||
|
public readonly Bindable<RoomDifficultyRange> DifficultyRange = new Bindable<RoomDifficultyRange>();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<RoomCategory> Category = new Bindable<RoomCategory>();
|
public readonly Bindable<RoomCategory> Category = new Bindable<RoomCategory>();
|
||||||
|
|
||||||
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
|
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
|
||||||
@ -51,19 +62,15 @@ namespace osu.Game.Online.Rooms
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<int?> MaxAttempts = new Bindable<int?>();
|
public readonly Bindable<int?> MaxAttempts = new Bindable<int?>();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>(new RoomStatusOpen());
|
public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>(new RoomStatusOpen());
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<RoomAvailability> Availability = new Bindable<RoomAvailability>();
|
public readonly Bindable<RoomAvailability> Availability = new Bindable<RoomAvailability>();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<MatchType> Type = new Bindable<MatchType>();
|
public readonly Bindable<MatchType> Type = new Bindable<MatchType>();
|
||||||
|
|
||||||
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
|
// Todo: osu-framework bug (https://github.com/ppy/osu-framework/issues/4106)
|
||||||
@ -76,7 +83,6 @@ namespace osu.Game.Online.Rooms
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<QueueMode> QueueMode = new Bindable<QueueMode>();
|
public readonly Bindable<QueueMode> QueueMode = new Bindable<QueueMode>();
|
||||||
|
|
||||||
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
[JsonConverter(typeof(SnakeCaseStringEnumConverter))]
|
||||||
@ -88,7 +94,6 @@ namespace osu.Game.Online.Rooms
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<int?> MaxParticipants = new Bindable<int?>();
|
public readonly Bindable<int?> MaxParticipants = new Bindable<int?>();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
@ -113,7 +118,6 @@ namespace osu.Game.Online.Rooms
|
|||||||
public readonly Bindable<string> Password = new Bindable<string>();
|
public readonly Bindable<string> Password = new Bindable<string>();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
[JsonIgnore]
|
|
||||||
public readonly Bindable<TimeSpan?> Duration = new Bindable<TimeSpan?>();
|
public readonly Bindable<TimeSpan?> Duration = new Bindable<TimeSpan?>();
|
||||||
|
|
||||||
[JsonProperty("duration")]
|
[JsonProperty("duration")]
|
||||||
@ -158,6 +162,8 @@ namespace osu.Game.Online.Rooms
|
|||||||
var copy = new Room();
|
var copy = new Room();
|
||||||
|
|
||||||
copy.CopyFrom(this);
|
copy.CopyFrom(this);
|
||||||
|
|
||||||
|
// ID must be unset as we use this as a marker for whether this is a client-side (not-yet-created) room or not.
|
||||||
copy.RoomID.Value = null;
|
copy.RoomID.Value = null;
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
@ -183,6 +189,9 @@ namespace osu.Game.Online.Rooms
|
|||||||
EndDate.Value = other.EndDate.Value;
|
EndDate.Value = other.EndDate.Value;
|
||||||
UserScore.Value = other.UserScore.Value;
|
UserScore.Value = other.UserScore.Value;
|
||||||
QueueMode.Value = other.QueueMode.Value;
|
QueueMode.Value = other.QueueMode.Value;
|
||||||
|
DifficultyRange.Value = other.DifficultyRange.Value;
|
||||||
|
PlaylistItemStats.Value = other.PlaylistItemStats.Value;
|
||||||
|
CurrentPlaylistItem.Value = other.CurrentPlaylistItem.Value;
|
||||||
|
|
||||||
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
|
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
|
||||||
Status.Value = new RoomStatusEnded();
|
Status.Value = new RoomStatusEnded();
|
||||||
@ -211,21 +220,27 @@ namespace osu.Game.Online.Rooms
|
|||||||
Playlist.RemoveAll(i => i.Expired);
|
Playlist.RemoveAll(i => i.Expired);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Newtonsoft.Json implicit ShouldSerialize() methods
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
|
public class RoomPlaylistItemStats
|
||||||
|
{
|
||||||
|
[JsonProperty("count_active")]
|
||||||
|
public int CountActive;
|
||||||
|
|
||||||
// The properties in this region are used implicitly by Newtonsoft.Json to not serialise certain fields in some cases.
|
[JsonProperty("count_total")]
|
||||||
// They rely on being named exactly the same as the corresponding fields (casing included) and as such should NOT be renamed
|
public int CountTotal;
|
||||||
// unless the fields are also renamed.
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
[JsonProperty("ruleset_ids")]
|
||||||
public bool ShouldSerializeRoomID() => false;
|
public int[] RulesetIDs;
|
||||||
|
}
|
||||||
|
|
||||||
[UsedImplicitly]
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
public bool ShouldSerializeHost() => false;
|
public class RoomDifficultyRange
|
||||||
|
{
|
||||||
|
[JsonProperty("min")]
|
||||||
|
public double Min;
|
||||||
|
|
||||||
[UsedImplicitly]
|
[JsonProperty("max")]
|
||||||
public bool ShouldSerializeEndDate() => false;
|
public double Max;
|
||||||
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Online
|
|||||||
|
|
||||||
// This should not be required. The fallback should work. But something is weird with the way caching is done.
|
// This should not be required. The fallback should work. But something is weird with the way caching is done.
|
||||||
// For future adventurers, I would not advise looking into this further. It's likely not worth the effort.
|
// For future adventurers, I would not advise looking into this further. It's likely not worth the effort.
|
||||||
baseMap = baseMap.Concat(baseMap.Select(t => (t.baseType, t.baseType)));
|
baseMap = baseMap.Concat(baseMap.Select(t => (t.baseType, t.baseType)).Distinct());
|
||||||
|
|
||||||
return new Dictionary<Type, IMessagePackFormatter>(baseMap.Select(t =>
|
return new Dictionary<Type, IMessagePackFormatter>(baseMap.Select(t =>
|
||||||
{
|
{
|
||||||
|
@ -150,6 +150,7 @@ namespace osu.Game
|
|||||||
protected SettingsOverlay Settings;
|
protected SettingsOverlay Settings;
|
||||||
|
|
||||||
private VolumeOverlay volume;
|
private VolumeOverlay volume;
|
||||||
|
|
||||||
private OsuLogo osuLogo;
|
private OsuLogo osuLogo;
|
||||||
|
|
||||||
private MainMenu menuScreen;
|
private MainMenu menuScreen;
|
||||||
@ -837,7 +838,8 @@ namespace osu.Game
|
|||||||
channelManager.HighPollRate.Value =
|
channelManager.HighPollRate.Value =
|
||||||
chatOverlay.State.Value == Visibility.Visible
|
chatOverlay.State.Value == Visibility.Visible
|
||||||
|| API.Activity.Value is UserActivity.InLobby
|
|| API.Activity.Value is UserActivity.InLobby
|
||||||
|| API.Activity.Value is UserActivity.InMultiplayerGame;
|
|| API.Activity.Value is UserActivity.InMultiplayerGame
|
||||||
|
|| API.Activity.Value is UserActivity.SpectatingMultiplayerGame;
|
||||||
}
|
}
|
||||||
|
|
||||||
Add(difficultyRecommender);
|
Add(difficultyRecommender);
|
||||||
@ -898,10 +900,22 @@ namespace osu.Game
|
|||||||
if (args?.Length > 0)
|
if (args?.Length > 0)
|
||||||
{
|
{
|
||||||
string[] paths = args.Where(a => !a.StartsWith('-')).ToArray();
|
string[] paths = args.Where(a => !a.StartsWith('-')).ToArray();
|
||||||
|
|
||||||
if (paths.Length > 0)
|
if (paths.Length > 0)
|
||||||
|
{
|
||||||
|
string firstPath = paths.First();
|
||||||
|
|
||||||
|
if (firstPath.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
HandleLink(firstPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Task.Run(() => Import(paths));
|
Task.Run(() => Import(paths));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays)
|
private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays)
|
||||||
{
|
{
|
||||||
|
@ -22,6 +22,7 @@ using osu.Framework.Logging;
|
|||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -52,6 +53,8 @@ namespace osu.Game
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles
|
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles
|
||||||
{
|
{
|
||||||
|
public const string OSU_PROTOCOL = "osu://";
|
||||||
|
|
||||||
public const string CLIENT_STREAM_NAME = @"lazer";
|
public const string CLIENT_STREAM_NAME = @"lazer";
|
||||||
|
|
||||||
public const int SAMPLE_CONCURRENCY = 6;
|
public const int SAMPLE_CONCURRENCY = 6;
|
||||||
@ -109,7 +112,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
protected SkinManager SkinManager { get; private set; }
|
protected SkinManager SkinManager { get; private set; }
|
||||||
|
|
||||||
protected RulesetStore RulesetStore { get; private set; }
|
protected RealmRulesetStore RulesetStore { get; private set; }
|
||||||
|
|
||||||
protected RealmKeyBindingStore KeyBindingStore { get; private set; }
|
protected RealmKeyBindingStore KeyBindingStore { get; private set; }
|
||||||
|
|
||||||
@ -200,9 +203,11 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.Cache(realm = new RealmAccess(Storage, "client", EFContextFactory));
|
dependencies.Cache(realm = new RealmAccess(Storage, "client", EFContextFactory));
|
||||||
|
|
||||||
dependencies.Cache(RulesetStore = new RulesetStore(realm, Storage));
|
dependencies.CacheAs<RulesetStore>(RulesetStore = new RealmRulesetStore(realm, Storage));
|
||||||
dependencies.CacheAs<IRulesetStore>(RulesetStore);
|
dependencies.CacheAs<IRulesetStore>(RulesetStore);
|
||||||
|
|
||||||
|
Decoder.RegisterDependencies(RulesetStore);
|
||||||
|
|
||||||
// Backup is taken here rather than in EFToRealmMigrator to avoid recycling realm contexts
|
// Backup is taken here rather than in EFToRealmMigrator to avoid recycling realm contexts
|
||||||
// after initial usages below. It can be moved once a direction is established for handling re-subscription.
|
// after initial usages below. It can be moved once a direction is established for handling re-subscription.
|
||||||
// See https://github.com/ppy/osu/pull/16547 for more discussion.
|
// See https://github.com/ppy/osu/pull/16547 for more discussion.
|
||||||
|
@ -183,7 +183,14 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
}
|
}
|
||||||
|
|
||||||
starRatingContainer.FadeOut(100);
|
starRatingContainer.FadeOut(100);
|
||||||
Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap;
|
|
||||||
|
// If a selection is already made, try and maintain it.
|
||||||
|
if (Beatmap.Value != null)
|
||||||
|
Beatmap.Value = Difficulties.FirstOrDefault(b => b.Beatmap.OnlineID == Beatmap.Value.OnlineID)?.Beatmap;
|
||||||
|
|
||||||
|
// Else just choose the first available difficulty for now.
|
||||||
|
Beatmap.Value ??= Difficulties.FirstOrDefault()?.Beatmap;
|
||||||
|
|
||||||
plays.Value = BeatmapSet?.PlayCount ?? 0;
|
plays.Value = BeatmapSet?.PlayCount ?? 0;
|
||||||
favourites.Value = BeatmapSet?.FavouriteCount ?? 0;
|
favourites.Value = BeatmapSet?.FavouriteCount ?? 0;
|
||||||
|
|
||||||
|
@ -8,11 +8,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -66,52 +62,5 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override ITooltip<Mod> GetCustomTooltip() => new IncompatibilityDisplayingTooltip();
|
public override ITooltip<Mod> GetCustomTooltip() => new IncompatibilityDisplayingTooltip();
|
||||||
|
|
||||||
private class IncompatibilityDisplayingTooltip : ModButtonTooltip
|
|
||||||
{
|
|
||||||
private readonly OsuSpriteText incompatibleText;
|
|
||||||
|
|
||||||
private readonly Bindable<IReadOnlyList<Mod>> incompatibleMods = new Bindable<IReadOnlyList<Mod>>();
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private Bindable<RulesetInfo> ruleset { get; set; }
|
|
||||||
|
|
||||||
public IncompatibilityDisplayingTooltip()
|
|
||||||
{
|
|
||||||
AddRange(new Drawable[]
|
|
||||||
{
|
|
||||||
incompatibleText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Margin = new MarginPadding { Top = 5 },
|
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
|
||||||
Text = "Incompatible with:"
|
|
||||||
},
|
|
||||||
new ModDisplay
|
|
||||||
{
|
|
||||||
Current = incompatibleMods,
|
|
||||||
ExpansionMode = ExpansionMode.AlwaysExpanded,
|
|
||||||
Scale = new Vector2(0.7f)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
incompatibleText.Colour = colours.BlueLight;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateDisplay(Mod mod)
|
|
||||||
{
|
|
||||||
base.UpdateDisplay(mod);
|
|
||||||
|
|
||||||
var incompatibleTypes = mod.IncompatibleMods;
|
|
||||||
|
|
||||||
var allMods = ruleset.Value.CreateInstance().AllMods;
|
|
||||||
|
|
||||||
incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).Select(m => m.CreateInstance()).ToList();
|
|
||||||
incompatibleText.Text = incompatibleMods.Value.Any() ? "Incompatible with:" : "Compatible with all mods";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
88
osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs
Normal file
88
osu.Game/Overlays/Mods/IncompatibilityDisplayingModPanel.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Mods
|
||||||
|
{
|
||||||
|
public class IncompatibilityDisplayingModPanel : ModPanel, IHasCustomTooltip<Mod>
|
||||||
|
{
|
||||||
|
private readonly BindableBool incompatible = new BindableBool();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; }
|
||||||
|
|
||||||
|
public IncompatibilityDisplayingModPanel(Mod mod)
|
||||||
|
: base(mod)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
selectedMods.BindValueChanged(_ => updateIncompatibility(), true);
|
||||||
|
incompatible.BindValueChanged(_ => Scheduler.AddOnce(UpdateState));
|
||||||
|
// base call will run `UpdateState()` first time and finish transforms.
|
||||||
|
base.LoadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateIncompatibility()
|
||||||
|
{
|
||||||
|
incompatible.Value = selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(Mod) && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState()
|
||||||
|
{
|
||||||
|
Action = incompatible.Value ? () => { } : (Action)Active.Toggle;
|
||||||
|
|
||||||
|
if (incompatible.Value)
|
||||||
|
{
|
||||||
|
Colour4 backgroundColour = ColourProvider.Background5;
|
||||||
|
Colour4 textBackgroundColour = ColourProvider.Background4;
|
||||||
|
|
||||||
|
Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, textBackgroundColour), TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
Background.FadeColour(backgroundColour, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
|
||||||
|
SwitchContainer.ResizeWidthTo(IDLE_SWITCH_WIDTH, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
SwitchContainer.FadeColour(Colour4.Gray, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
MainContentContainer.TransformTo(nameof(Padding), new MarginPadding
|
||||||
|
{
|
||||||
|
Left = IDLE_SWITCH_WIDTH,
|
||||||
|
Right = CORNER_RADIUS
|
||||||
|
}, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
|
||||||
|
TextBackground.FadeColour(textBackgroundColour, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
TextFlow.FadeColour(Colour4.White.Opacity(0.5f), TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchContainer.FadeColour(Colour4.White, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
base.UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
if (incompatible.Value)
|
||||||
|
return true; // bypasses base call purposely in order to not play out the intermediate state animation.
|
||||||
|
|
||||||
|
return base.OnMouseDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IHasCustomTooltip
|
||||||
|
|
||||||
|
public ITooltip<Mod> GetCustomTooltip() => new IncompatibilityDisplayingTooltip();
|
||||||
|
|
||||||
|
public Mod TooltipContent => Mod;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
64
osu.Game/Overlays/Mods/IncompatibilityDisplayingTooltip.cs
Normal file
64
osu.Game/Overlays/Mods/IncompatibilityDisplayingTooltip.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Mods
|
||||||
|
{
|
||||||
|
internal class IncompatibilityDisplayingTooltip : ModButtonTooltip
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText incompatibleText;
|
||||||
|
|
||||||
|
private readonly Bindable<IReadOnlyList<Mod>> incompatibleMods = new Bindable<IReadOnlyList<Mod>>();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
|
public IncompatibilityDisplayingTooltip()
|
||||||
|
{
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
incompatibleText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Margin = new MarginPadding { Top = 5 },
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||||
|
Text = "Incompatible with:"
|
||||||
|
},
|
||||||
|
new ModDisplay
|
||||||
|
{
|
||||||
|
Current = incompatibleMods,
|
||||||
|
ExpansionMode = ExpansionMode.AlwaysExpanded,
|
||||||
|
Scale = new Vector2(0.7f)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
incompatibleText.Colour = colours.BlueLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateDisplay(Mod mod)
|
||||||
|
{
|
||||||
|
base.UpdateDisplay(mod);
|
||||||
|
|
||||||
|
var incompatibleTypes = mod.IncompatibleMods;
|
||||||
|
|
||||||
|
var allMods = ruleset.Value.CreateInstance().AllMods;
|
||||||
|
|
||||||
|
incompatibleMods.Value = allMods.Where(m => m.GetType() != mod.GetType() && incompatibleTypes.Any(t => t.IsInstanceOfType(m))).Select(m => m.CreateInstance()).ToList();
|
||||||
|
incompatibleText.Text = incompatibleMods.Value.Any() ? "Incompatible with:" : "Compatible with all mods";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
239
osu.Game/Overlays/Mods/ModPanel.cs
Normal file
239
osu.Game/Overlays/Mods/ModPanel.cs
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Mods
|
||||||
|
{
|
||||||
|
public class ModPanel : OsuClickableContainer
|
||||||
|
{
|
||||||
|
public Mod Mod { get; }
|
||||||
|
public BindableBool Active { get; } = new BindableBool();
|
||||||
|
|
||||||
|
protected readonly Box Background;
|
||||||
|
protected readonly Container SwitchContainer;
|
||||||
|
protected readonly Container MainContentContainer;
|
||||||
|
protected readonly Box TextBackground;
|
||||||
|
protected readonly FillFlowContainer TextFlow;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
protected OverlayColourProvider ColourProvider { get; private set; } = null!;
|
||||||
|
|
||||||
|
protected const double TRANSITION_DURATION = 150;
|
||||||
|
|
||||||
|
protected const float SHEAR_X = 0.2f;
|
||||||
|
|
||||||
|
protected const float HEIGHT = 42;
|
||||||
|
protected const float CORNER_RADIUS = 7;
|
||||||
|
protected const float IDLE_SWITCH_WIDTH = 54;
|
||||||
|
protected const float EXPANDED_SWITCH_WIDTH = 70;
|
||||||
|
|
||||||
|
private Colour4 activeColour;
|
||||||
|
|
||||||
|
private Sample? sampleOff;
|
||||||
|
private Sample? sampleOn;
|
||||||
|
|
||||||
|
public ModPanel(Mod mod)
|
||||||
|
{
|
||||||
|
Mod = mod;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 42;
|
||||||
|
|
||||||
|
// all below properties are applied to `Content` rather than the `ModPanel` in its entirety
|
||||||
|
// to allow external components to set these properties on the panel without affecting
|
||||||
|
// its "internal" appearance.
|
||||||
|
Content.Masking = true;
|
||||||
|
Content.CornerRadius = CORNER_RADIUS;
|
||||||
|
Content.BorderThickness = 2;
|
||||||
|
Content.Shear = new Vector2(SHEAR_X, 0);
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
Background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
SwitchContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = new ModSwitchSmall(mod)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Active = { BindTarget = Active },
|
||||||
|
Shear = new Vector2(-SHEAR_X, 0),
|
||||||
|
Scale = new Vector2(HEIGHT / ModSwitchSmall.DEFAULT_SIZE)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MainContentContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = CORNER_RADIUS,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
TextBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
TextFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = 17.5f,
|
||||||
|
Vertical = 4
|
||||||
|
},
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = mod.Name,
|
||||||
|
Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold),
|
||||||
|
Shear = new Vector2(-SHEAR_X, 0),
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = -18 * SHEAR_X
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = mod.Description,
|
||||||
|
Font = OsuFont.Default.With(size: 12),
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Truncate = true,
|
||||||
|
Shear = new Vector2(-SHEAR_X, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Action = Active.Toggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio, OsuColour colours)
|
||||||
|
{
|
||||||
|
sampleOn = audio.Samples.Get(@"UI/check-on");
|
||||||
|
sampleOff = audio.Samples.Get(@"UI/check-off");
|
||||||
|
|
||||||
|
activeColour = colours.ForModType(Mod.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
Active.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
playStateChangeSamples();
|
||||||
|
UpdateState();
|
||||||
|
});
|
||||||
|
|
||||||
|
UpdateState();
|
||||||
|
FinishTransforms(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playStateChangeSamples()
|
||||||
|
{
|
||||||
|
if (Active.Value)
|
||||||
|
sampleOn?.Play();
|
||||||
|
else
|
||||||
|
sampleOff?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
UpdateState();
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
UpdateState();
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool mouseDown;
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButton.Left)
|
||||||
|
mouseDown = true;
|
||||||
|
|
||||||
|
UpdateState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
mouseDown = false;
|
||||||
|
|
||||||
|
UpdateState();
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateState()
|
||||||
|
{
|
||||||
|
float targetWidth = Active.Value ? EXPANDED_SWITCH_WIDTH : IDLE_SWITCH_WIDTH;
|
||||||
|
double transitionDuration = TRANSITION_DURATION;
|
||||||
|
|
||||||
|
Colour4 textBackgroundColour = Active.Value ? activeColour : (Colour4)ColourProvider.Background2;
|
||||||
|
Colour4 mainBackgroundColour = Active.Value ? activeColour.Darken(0.3f) : (Colour4)ColourProvider.Background3;
|
||||||
|
Colour4 textColour = Active.Value ? (Colour4)ColourProvider.Background6 : Colour4.White;
|
||||||
|
|
||||||
|
// Hover affects colour of button background
|
||||||
|
if (IsHovered)
|
||||||
|
{
|
||||||
|
textBackgroundColour = textBackgroundColour.Lighten(0.1f);
|
||||||
|
mainBackgroundColour = mainBackgroundColour.Lighten(0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mouse down adds a halfway tween of the movement
|
||||||
|
if (mouseDown)
|
||||||
|
{
|
||||||
|
targetWidth = (float)Interpolation.Lerp(IDLE_SWITCH_WIDTH, EXPANDED_SWITCH_WIDTH, 0.5f);
|
||||||
|
transitionDuration *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(mainBackgroundColour, textBackgroundColour), transitionDuration, Easing.OutQuint);
|
||||||
|
Background.FadeColour(mainBackgroundColour, transitionDuration, Easing.OutQuint);
|
||||||
|
SwitchContainer.ResizeWidthTo(targetWidth, transitionDuration, Easing.OutQuint);
|
||||||
|
MainContentContainer.TransformTo(nameof(Padding), new MarginPadding
|
||||||
|
{
|
||||||
|
Left = targetWidth,
|
||||||
|
Right = CORNER_RADIUS
|
||||||
|
}, transitionDuration, Easing.OutQuint);
|
||||||
|
TextBackground.FadeColour(textBackgroundColour, transitionDuration, Easing.OutQuint);
|
||||||
|
TextFlow.FadeColour(textColour, transitionDuration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,17 +30,20 @@ namespace osu.Game.Overlays.Music
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private OnScreenDisplay onScreenDisplay { get; set; }
|
private OnScreenDisplay onScreenDisplay { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuGame game { get; set; }
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
if (e.Repeat)
|
if (e.Repeat)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (beatmap.Disabled)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case GlobalAction.MusicPlay:
|
case GlobalAction.MusicPlay:
|
||||||
|
if (game.LocalUserPlaying.Value)
|
||||||
|
return false;
|
||||||
|
|
||||||
// use previous state as TogglePause may not update the track's state immediately (state update is run on the audio thread see https://github.com/ppy/osu/issues/9880#issuecomment-674668842)
|
// use previous state as TogglePause may not update the track's state immediately (state update is run on the audio thread see https://github.com/ppy/osu/issues/9880#issuecomment-674668842)
|
||||||
bool wasPlaying = musicController.IsPlaying;
|
bool wasPlaying = musicController.IsPlaying;
|
||||||
|
|
||||||
@ -49,11 +52,17 @@ namespace osu.Game.Overlays.Music
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.MusicNext:
|
case GlobalAction.MusicNext:
|
||||||
|
if (beatmap.Disabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast(GlobalActionKeyBindingStrings.MusicNext, e.Action)));
|
musicController.NextTrack(() => onScreenDisplay?.Display(new MusicActionToast(GlobalActionKeyBindingStrings.MusicNext, e.Action)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.MusicPrev:
|
case GlobalAction.MusicPrev:
|
||||||
|
if (beatmap.Disabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
musicController.PreviousTrack(res =>
|
musicController.PreviousTrack(res =>
|
||||||
{
|
{
|
||||||
switch (res)
|
switch (res)
|
||||||
|
51
osu.Game/Rulesets/AssemblyRulesetStore.cs
Normal file
51
osu.Game/Rulesets/AssemblyRulesetStore.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A ruleset store that populates from loaded assemblies (and optionally, assemblies in a storage).
|
||||||
|
/// </summary>
|
||||||
|
public class AssemblyRulesetStore : RulesetStore
|
||||||
|
{
|
||||||
|
public override IEnumerable<RulesetInfo> AvailableRulesets => availableRulesets;
|
||||||
|
|
||||||
|
private readonly List<RulesetInfo> availableRulesets = new List<RulesetInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an assembly ruleset store that populates from loaded assemblies and an external location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">An path containing ruleset DLLs.</param>
|
||||||
|
public AssemblyRulesetStore(string path)
|
||||||
|
: this(new NativeStorage(path))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an assembly ruleset store that populates from loaded assemblies and an optional storage source.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">An optional storage containing ruleset DLLs.</param>
|
||||||
|
public AssemblyRulesetStore(Storage? storage = null)
|
||||||
|
: base(storage)
|
||||||
|
|
||||||
|
{
|
||||||
|
List<Ruleset> instances = LoadedAssemblies.Values
|
||||||
|
.Select(r => Activator.CreateInstance(r) as Ruleset)
|
||||||
|
.Where(r => r != null)
|
||||||
|
.Select(r => r.AsNonNull())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// add all legacy rulesets first to ensure they have exclusive choice of primary key.
|
||||||
|
foreach (var r in instances.Where(r => r is ILegacyRuleset))
|
||||||
|
availableRulesets.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
osu.Game/Rulesets/RealmRulesetStore.cs
Normal file
101
osu.Game/Rulesets/RealmRulesetStore.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Database;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets
|
||||||
|
{
|
||||||
|
public class RealmRulesetStore : RulesetStore
|
||||||
|
{
|
||||||
|
public override IEnumerable<RulesetInfo> AvailableRulesets => availableRulesets;
|
||||||
|
|
||||||
|
private readonly List<RulesetInfo> availableRulesets = new List<RulesetInfo>();
|
||||||
|
|
||||||
|
public RealmRulesetStore(RealmAccess realm, Storage? storage = null)
|
||||||
|
: base(storage)
|
||||||
|
{
|
||||||
|
prepareDetachedRulesets(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareDetachedRulesets(RealmAccess realmAccess)
|
||||||
|
{
|
||||||
|
realmAccess.Write(realm =>
|
||||||
|
{
|
||||||
|
var rulesets = realm.All<RulesetInfo>();
|
||||||
|
|
||||||
|
List<Ruleset> instances = LoadedAssemblies.Values
|
||||||
|
.Select(r => Activator.CreateInstance(r) as Ruleset)
|
||||||
|
.Where(r => r != null)
|
||||||
|
.Select(r => r.AsNonNull())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// add all legacy rulesets first to ensure they have exclusive choice of primary key.
|
||||||
|
foreach (var r in instances.Where(r => r is ILegacyRuleset))
|
||||||
|
{
|
||||||
|
if (realm.All<RulesetInfo>().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.OnlineID) == null)
|
||||||
|
realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add any other rulesets which have assemblies present but are not yet in the database.
|
||||||
|
foreach (var r in instances.Where(r => !(r is ILegacyRuleset)))
|
||||||
|
{
|
||||||
|
if (rulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null)
|
||||||
|
{
|
||||||
|
var existingSameShortName = rulesets.FirstOrDefault(ri => ri.ShortName == r.RulesetInfo.ShortName);
|
||||||
|
|
||||||
|
if (existingSameShortName != null)
|
||||||
|
{
|
||||||
|
// even if a matching InstantiationInfo was not found, there may be an existing ruleset with the same ShortName.
|
||||||
|
// this generally means the user or ruleset provider has renamed their dll but the underlying ruleset is *likely* the same one.
|
||||||
|
// in such cases, update the instantiation info of the existing entry to point to the new one.
|
||||||
|
existingSameShortName.InstantiationInfo = r.RulesetInfo.InstantiationInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RulesetInfo> detachedRulesets = new List<RulesetInfo>();
|
||||||
|
|
||||||
|
// perform a consistency check and detach final rulesets from realm for cross-thread runtime usage.
|
||||||
|
foreach (var r in rulesets.OrderBy(r => r.OnlineID))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var resolvedType = Type.GetType(r.InstantiationInfo)
|
||||||
|
?? throw new RulesetLoadException(@"Type could not be resolved");
|
||||||
|
|
||||||
|
var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
|
||||||
|
?? throw new RulesetLoadException(@"Instantiation failure");
|
||||||
|
|
||||||
|
// If a ruleset isn't up-to-date with the API, it could cause a crash at an arbitrary point of execution.
|
||||||
|
// To eagerly handle cases of missing implementations, enumerate all types here and mark as non-available on throw.
|
||||||
|
resolvedType.Assembly.GetTypes();
|
||||||
|
|
||||||
|
r.Name = instanceInfo.Name;
|
||||||
|
r.ShortName = instanceInfo.ShortName;
|
||||||
|
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
||||||
|
r.Available = true;
|
||||||
|
|
||||||
|
detachedRulesets.Add(r.Clone());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
r.Available = false;
|
||||||
|
Logger.Log($"Could not load ruleset {r}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableRulesets.AddRange(detachedRulesets.OrderBy(r => r));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,34 +7,26 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Database;
|
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
namespace osu.Game.Rulesets
|
namespace osu.Game.Rulesets
|
||||||
{
|
{
|
||||||
public class RulesetStore : IDisposable, IRulesetStore
|
public abstract class RulesetStore : IDisposable, IRulesetStore
|
||||||
{
|
{
|
||||||
private readonly RealmAccess realmAccess;
|
|
||||||
|
|
||||||
private const string ruleset_library_prefix = @"osu.Game.Rulesets";
|
private const string ruleset_library_prefix = @"osu.Game.Rulesets";
|
||||||
|
|
||||||
private readonly Dictionary<Assembly, Type> loadedAssemblies = new Dictionary<Assembly, Type>();
|
protected readonly Dictionary<Assembly, Type> LoadedAssemblies = new Dictionary<Assembly, Type>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All available rulesets.
|
/// All available rulesets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<RulesetInfo> AvailableRulesets => availableRulesets;
|
public abstract IEnumerable<RulesetInfo> AvailableRulesets { get; }
|
||||||
|
|
||||||
private readonly List<RulesetInfo> availableRulesets = new List<RulesetInfo>();
|
protected RulesetStore(Storage? storage = null)
|
||||||
|
|
||||||
public RulesetStore(RealmAccess realm, Storage? storage = null)
|
|
||||||
{
|
{
|
||||||
realmAccess = realm;
|
|
||||||
|
|
||||||
// On android in release configuration assemblies are loaded from the apk directly into memory.
|
// On android in release configuration assemblies are loaded from the apk directly into memory.
|
||||||
// We cannot read assemblies from cwd, so should check loaded assemblies instead.
|
// We cannot read assemblies from cwd, so should check loaded assemblies instead.
|
||||||
loadFromAppDomain();
|
loadFromAppDomain();
|
||||||
@ -53,8 +45,6 @@ namespace osu.Game.Rulesets
|
|||||||
var rulesetStorage = storage?.GetStorageForDirectory(@"rulesets");
|
var rulesetStorage = storage?.GetStorageForDirectory(@"rulesets");
|
||||||
if (rulesetStorage != null)
|
if (rulesetStorage != null)
|
||||||
loadUserRulesets(rulesetStorage);
|
loadUserRulesets(rulesetStorage);
|
||||||
|
|
||||||
addMissingRulesets();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -95,80 +85,7 @@ namespace osu.Game.Rulesets
|
|||||||
if (domainAssembly != null)
|
if (domainAssembly != null)
|
||||||
return domainAssembly;
|
return domainAssembly;
|
||||||
|
|
||||||
return loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName);
|
return LoadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName);
|
||||||
}
|
|
||||||
|
|
||||||
private void addMissingRulesets()
|
|
||||||
{
|
|
||||||
realmAccess.Write(realm =>
|
|
||||||
{
|
|
||||||
var rulesets = realm.All<RulesetInfo>();
|
|
||||||
|
|
||||||
List<Ruleset> instances = loadedAssemblies.Values
|
|
||||||
.Select(r => Activator.CreateInstance(r) as Ruleset)
|
|
||||||
.Where(r => r != null)
|
|
||||||
.Select(r => r.AsNonNull())
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// add all legacy rulesets first to ensure they have exclusive choice of primary key.
|
|
||||||
foreach (var r in instances.Where(r => r is ILegacyRuleset))
|
|
||||||
{
|
|
||||||
if (realm.All<RulesetInfo>().FirstOrDefault(rr => rr.OnlineID == r.RulesetInfo.OnlineID) == null)
|
|
||||||
realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID));
|
|
||||||
}
|
|
||||||
|
|
||||||
// add any other rulesets which have assemblies present but are not yet in the database.
|
|
||||||
foreach (var r in instances.Where(r => !(r is ILegacyRuleset)))
|
|
||||||
{
|
|
||||||
if (rulesets.FirstOrDefault(ri => ri.InstantiationInfo.Equals(r.RulesetInfo.InstantiationInfo, StringComparison.Ordinal)) == null)
|
|
||||||
{
|
|
||||||
var existingSameShortName = rulesets.FirstOrDefault(ri => ri.ShortName == r.RulesetInfo.ShortName);
|
|
||||||
|
|
||||||
if (existingSameShortName != null)
|
|
||||||
{
|
|
||||||
// even if a matching InstantiationInfo was not found, there may be an existing ruleset with the same ShortName.
|
|
||||||
// this generally means the user or ruleset provider has renamed their dll but the underlying ruleset is *likely* the same one.
|
|
||||||
// in such cases, update the instantiation info of the existing entry to point to the new one.
|
|
||||||
existingSameShortName.InstantiationInfo = r.RulesetInfo.InstantiationInfo;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
realm.Add(new RulesetInfo(r.RulesetInfo.ShortName, r.RulesetInfo.Name, r.RulesetInfo.InstantiationInfo, r.RulesetInfo.OnlineID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<RulesetInfo> detachedRulesets = new List<RulesetInfo>();
|
|
||||||
|
|
||||||
// perform a consistency check and detach final rulesets from realm for cross-thread runtime usage.
|
|
||||||
foreach (var r in rulesets.OrderBy(r => r.OnlineID))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var resolvedType = Type.GetType(r.InstantiationInfo)
|
|
||||||
?? throw new RulesetLoadException(@"Type could not be resolved");
|
|
||||||
|
|
||||||
var instanceInfo = (Activator.CreateInstance(resolvedType) as Ruleset)?.RulesetInfo
|
|
||||||
?? throw new RulesetLoadException(@"Instantiation failure");
|
|
||||||
|
|
||||||
// If a ruleset isn't up-to-date with the API, it could cause a crash at an arbitrary point of execution.
|
|
||||||
// To eagerly handle cases of missing implementations, enumerate all types here and mark as non-available on throw.
|
|
||||||
resolvedType.Assembly.GetTypes();
|
|
||||||
|
|
||||||
r.Name = instanceInfo.Name;
|
|
||||||
r.ShortName = instanceInfo.ShortName;
|
|
||||||
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
|
||||||
r.Available = true;
|
|
||||||
|
|
||||||
detachedRulesets.Add(r.Clone());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
r.Available = false;
|
|
||||||
Logger.Log($"Could not load ruleset {r}: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
availableRulesets.AddRange(detachedRulesets.OrderBy(r => r));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFromAppDomain()
|
private void loadFromAppDomain()
|
||||||
@ -214,7 +131,7 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
string? filename = Path.GetFileNameWithoutExtension(file);
|
string? filename = Path.GetFileNameWithoutExtension(file);
|
||||||
|
|
||||||
if (loadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename))
|
if (LoadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -229,17 +146,17 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
private void addRuleset(Assembly assembly)
|
private void addRuleset(Assembly assembly)
|
||||||
{
|
{
|
||||||
if (loadedAssemblies.ContainsKey(assembly))
|
if (LoadedAssemblies.ContainsKey(assembly))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// the same assembly may be loaded twice in the same AppDomain (currently a thing in certain Rider versions https://youtrack.jetbrains.com/issue/RIDER-48799).
|
// the same assembly may be loaded twice in the same AppDomain (currently a thing in certain Rider versions https://youtrack.jetbrains.com/issue/RIDER-48799).
|
||||||
// as a failsafe, also compare by FullName.
|
// as a failsafe, also compare by FullName.
|
||||||
if (loadedAssemblies.Any(a => a.Key.FullName == assembly.FullName))
|
if (LoadedAssemblies.Any(a => a.Key.FullName == assembly.FullName))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
loadedAssemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset)));
|
LoadedAssemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset)));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
109
osu.Game/Rulesets/UI/ModSwitchSmall.cs
Normal file
109
osu.Game/Rulesets/UI/ModSwitchSmall.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI
|
||||||
|
{
|
||||||
|
public class ModSwitchSmall : CompositeDrawable
|
||||||
|
{
|
||||||
|
public BindableBool Active { get; } = new BindableBool();
|
||||||
|
|
||||||
|
public const float DEFAULT_SIZE = 60;
|
||||||
|
|
||||||
|
private readonly IMod mod;
|
||||||
|
|
||||||
|
private readonly SpriteIcon background;
|
||||||
|
private readonly SpriteIcon? modIcon;
|
||||||
|
|
||||||
|
private Color4 activeForegroundColour;
|
||||||
|
private Color4 inactiveForegroundColour;
|
||||||
|
|
||||||
|
private Color4 activeBackgroundColour;
|
||||||
|
private Color4 inactiveBackgroundColour;
|
||||||
|
|
||||||
|
public ModSwitchSmall(IMod mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
FillFlowContainer contentFlow;
|
||||||
|
ModSwitchTiny tinySwitch;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(DEFAULT_SIZE),
|
||||||
|
Icon = OsuIcon.ModBg
|
||||||
|
},
|
||||||
|
contentFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(0, 4),
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Child = tinySwitch = new ModSwitchTiny(mod)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Scale = new Vector2(0.6f),
|
||||||
|
Active = { BindTarget = Active }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mod.Icon != null)
|
||||||
|
{
|
||||||
|
contentFlow.Insert(-1, modIcon = new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Size = new Vector2(21),
|
||||||
|
Icon = mod.Icon.Value
|
||||||
|
});
|
||||||
|
tinySwitch.Scale = new Vector2(0.3f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuColour colours, OverlayColourProvider? colourProvider)
|
||||||
|
{
|
||||||
|
inactiveForegroundColour = colourProvider?.Background5 ?? colours.Gray3;
|
||||||
|
activeForegroundColour = colours.ForModType(mod.Type);
|
||||||
|
|
||||||
|
inactiveBackgroundColour = colourProvider?.Background2 ?? colours.Gray5;
|
||||||
|
activeBackgroundColour = Interpolation.ValueAt<Colour4>(0.1f, Colour4.Black, activeForegroundColour, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Active.BindValueChanged(_ => updateState(), true);
|
||||||
|
FinishTransforms(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
modIcon?.FadeColour(Active.Value ? activeForegroundColour : inactiveForegroundColour, 200, Easing.OutQuint);
|
||||||
|
background.FadeColour(Active.Value ? activeBackgroundColour : inactiveBackgroundColour, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
osu.Game/Rulesets/UI/ModSwitchTiny.cs
Normal file
93
osu.Game/Rulesets/UI/ModSwitchTiny.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI
|
||||||
|
{
|
||||||
|
public class ModSwitchTiny : CompositeDrawable
|
||||||
|
{
|
||||||
|
public BindableBool Active { get; } = new BindableBool();
|
||||||
|
|
||||||
|
public const float DEFAULT_HEIGHT = 30;
|
||||||
|
|
||||||
|
private readonly IMod mod;
|
||||||
|
|
||||||
|
private readonly Box background;
|
||||||
|
private readonly OsuSpriteText acronymText;
|
||||||
|
|
||||||
|
private Color4 activeForegroundColour;
|
||||||
|
private Color4 inactiveForegroundColour;
|
||||||
|
|
||||||
|
private Color4 activeBackgroundColour;
|
||||||
|
private Color4 inactiveBackgroundColour;
|
||||||
|
|
||||||
|
public ModSwitchTiny(IMod mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
Size = new Vector2(73, DEFAULT_HEIGHT);
|
||||||
|
|
||||||
|
InternalChild = new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
acronymText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Shadow = false,
|
||||||
|
Font = OsuFont.Numeric.With(size: 24, weight: FontWeight.Black),
|
||||||
|
Text = mod.Acronym,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuColour colours, OverlayColourProvider? colourProvider)
|
||||||
|
{
|
||||||
|
inactiveBackgroundColour = colourProvider?.Background5 ?? colours.Gray3;
|
||||||
|
activeBackgroundColour = colours.ForModType(mod.Type);
|
||||||
|
|
||||||
|
inactiveForegroundColour = colourProvider?.Background2 ?? colours.Gray5;
|
||||||
|
activeForegroundColour = Interpolation.ValueAt<Colour4>(0.1f, Colour4.Black, activeForegroundColour, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Active.BindValueChanged(_ => updateState(), true);
|
||||||
|
FinishTransforms(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
acronymText.FadeColour(Active.Value ? activeForegroundColour : inactiveForegroundColour, 200, Easing.OutQuint);
|
||||||
|
background.FadeColour(Active.Value ? activeBackgroundColour : inactiveBackgroundColour, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,19 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -25,7 +29,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Menu
|
namespace osu.Game.Screens.Menu
|
||||||
{
|
{
|
||||||
public class MainMenu : OsuScreen, IHandlePresentBeatmap
|
public class MainMenu : OsuScreen, IHandlePresentBeatmap, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
public const float FADE_IN_DURATION = 300;
|
public const float FADE_IN_DURATION = 300;
|
||||||
|
|
||||||
@ -297,5 +301,26 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
Schedule(loadSoloSongSelect);
|
Schedule(loadSoloSongSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
if (e.Repeat)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case GlobalAction.Back:
|
||||||
|
// In the case of a host being able to exit, the back action is handled by ExitConfirmOverlay.
|
||||||
|
Debug.Assert(!host.CanExit);
|
||||||
|
|
||||||
|
return host.SuspendToBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
{
|
{
|
||||||
InternalChild = sprite = CreateBackgroundSprite();
|
InternalChild = sprite = CreateBackgroundSprite();
|
||||||
|
|
||||||
|
CurrentPlaylistItem.BindValueChanged(_ => updateBeatmap());
|
||||||
Playlist.CollectionChanged += (_, __) => updateBeatmap();
|
Playlist.CollectionChanged += (_, __) => updateBeatmap();
|
||||||
|
|
||||||
updateBeatmap();
|
updateBeatmap();
|
||||||
@ -30,7 +31,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
|
|
||||||
private void updateBeatmap()
|
private void updateBeatmap()
|
||||||
{
|
{
|
||||||
sprite.Beatmap.Value = Playlist.GetCurrentItem()?.Beatmap;
|
sprite.Beatmap.Value = CurrentPlaylistItem.Value?.Beatmap ?? Playlist.GetCurrentItem()?.Beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -75,15 +74,29 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Playlist.BindCollectionChanged(updateRange, true);
|
DifficultyRange.BindValueChanged(_ => updateRange());
|
||||||
|
Playlist.BindCollectionChanged((_, __) => updateRange(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRange(object sender, NotifyCollectionChangedEventArgs e)
|
private void updateRange()
|
||||||
{
|
{
|
||||||
|
StarDifficulty minDifficulty;
|
||||||
|
StarDifficulty maxDifficulty;
|
||||||
|
|
||||||
|
if (DifficultyRange.Value != null)
|
||||||
|
{
|
||||||
|
minDifficulty = new StarDifficulty(DifficultyRange.Value.Min, 0);
|
||||||
|
maxDifficulty = new StarDifficulty(DifficultyRange.Value.Max, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In multiplayer rooms, the beatmaps of playlist items will not be populated to a point this can be correct.
|
||||||
|
// Either populating them via BeatmapLookupCache or polling the API for the room's DifficultyRange will be required.
|
||||||
var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
|
var orderedDifficulties = Playlist.Select(p => p.Beatmap).OrderBy(b => b.StarRating).ToArray();
|
||||||
|
|
||||||
StarDifficulty minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0);
|
minDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[0].StarRating : 0, 0);
|
||||||
StarDifficulty maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0);
|
maxDifficulty = new StarDifficulty(orderedDifficulties.Length > 0 ? orderedDifficulties[^1].StarRating : 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
minDisplay.Current.Value = minDifficulty;
|
minDisplay.Current.Value = minDifficulty;
|
||||||
maxDisplay.Current.Value = maxDifficulty;
|
maxDisplay.Current.Value = maxDifficulty;
|
||||||
|
@ -388,7 +388,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
SelectedItem.BindValueChanged(onSelectedItemChanged, true);
|
CurrentPlaylistItem.BindValueChanged(onSelectedItemChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CancellationTokenSource beatmapLookupCancellation;
|
private CancellationTokenSource beatmapLookupCancellation;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Specialized;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
@ -41,15 +41,22 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Playlist.BindCollectionChanged(updateCount, true);
|
PlaylistItemStats.BindValueChanged(_ => updateCount());
|
||||||
|
Playlist.BindCollectionChanged((_, __) => updateCount(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCount(object sender, NotifyCollectionChangedEventArgs e)
|
private void updateCount()
|
||||||
{
|
{
|
||||||
|
int activeItems = Playlist.Count > 0 || PlaylistItemStats.Value == null
|
||||||
|
// For now, use the playlist as the source of truth if it has any items.
|
||||||
|
// This allows the count to display correctly on the room screen (after joining a room).
|
||||||
|
? Playlist.Count(i => !i.Expired)
|
||||||
|
: PlaylistItemStats.Value.CountActive;
|
||||||
|
|
||||||
count.Clear();
|
count.Clear();
|
||||||
count.AddText(Playlist.Count.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold));
|
count.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold));
|
||||||
count.AddText(" ");
|
count.AddText(" ");
|
||||||
count.AddText("Beatmap".ToQuantity(Playlist.Count, ShowQuantityAs.None));
|
count.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
{
|
{
|
||||||
bool matchingFilter = true;
|
bool matchingFilter = true;
|
||||||
|
|
||||||
matchingFilter &= r.Room.Playlist.Count == 0 || criteria.Ruleset == null || r.Room.Playlist.Any(i => i.RulesetID == criteria.Ruleset.OnlineID);
|
matchingFilter &= criteria.Ruleset == null || r.Room.PlaylistItemStats.Value?.RulesetIDs.Any(id => id == criteria.Ruleset.OnlineID) != false;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(criteria.SearchString))
|
if (!string.IsNullOrEmpty(criteria.SearchString))
|
||||||
matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase));
|
matchingFilter &= r.FilterTerms.Any(term => term.Contains(criteria.SearchString, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -15,10 +16,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
{
|
{
|
||||||
public class GameplayChatDisplay : MatchChatDisplay, IKeyBindingHandler<GlobalAction>
|
public class GameplayChatDisplay : MatchChatDisplay, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved(CanBeNull = true)]
|
||||||
|
[CanBeNull]
|
||||||
private ILocalUserPlayInfo localUserInfo { get; set; }
|
private ILocalUserPlayInfo localUserInfo { get; set; }
|
||||||
|
|
||||||
private IBindable<bool> localUserPlaying = new Bindable<bool>();
|
private readonly IBindable<bool> localUserPlaying = new Bindable<bool>();
|
||||||
|
|
||||||
public override bool PropagatePositionalInputSubTree => !localUserPlaying.Value;
|
public override bool PropagatePositionalInputSubTree => !localUserPlaying.Value;
|
||||||
|
|
||||||
@ -46,7 +48,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy();
|
if (localUserInfo != null)
|
||||||
|
localUserPlaying.BindTo(localUserInfo.IsPlaying);
|
||||||
|
|
||||||
localUserPlaying.BindValueChanged(playing =>
|
localUserPlaying.BindValueChanged(playing =>
|
||||||
{
|
{
|
||||||
// for now let's never hold focus. this avoid misdirected gameplay keys entering chat.
|
// for now let's never hold focus. this avoid misdirected gameplay keys entering chat.
|
||||||
|
@ -343,7 +343,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
drawablePlaylist.Items.BindTo(Playlist);
|
drawablePlaylist.Items.BindTo(Playlist);
|
||||||
drawablePlaylist.SelectedItem.BindTo(SelectedItem);
|
drawablePlaylist.SelectedItem.BindTo(CurrentPlaylistItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -419,7 +419,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
if (text.StartsWith(not_found_prefix, StringComparison.Ordinal))
|
if (text.StartsWith(not_found_prefix, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ErrorText.Text = "The selected beatmap is not available online.";
|
ErrorText.Text = "The selected beatmap is not available online.";
|
||||||
SelectedItem.Value.MarkInvalid();
|
CurrentPlaylistItem.Value.MarkInvalid();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
SelectedItem.BindValueChanged(_ => updateState());
|
CurrentPlaylistItem.BindValueChanged(_ => updateState());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRoomUpdated()
|
protected override void OnRoomUpdated()
|
||||||
@ -111,7 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
|
|
||||||
bool enableButton =
|
bool enableButton =
|
||||||
Room?.State == MultiplayerRoomState.Open
|
Room?.State == MultiplayerRoomState.Open
|
||||||
&& SelectedItem.Value?.ID == Room.Settings.PlaylistItemId
|
&& CurrentPlaylistItem.Value?.ID == Room.Settings.PlaylistItemId
|
||||||
&& !Room.Playlist.Single(i => i.ID == Room.Settings.PlaylistItemId).Expired
|
&& !Room.Playlist.Single(i => i.ID == Room.Settings.PlaylistItemId).Expired
|
||||||
&& !operationInProgress.Value;
|
&& !operationInProgress.Value;
|
||||||
|
|
||||||
|
@ -52,14 +52,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match.Playlist
|
|||||||
queueList = new MultiplayerQueueList
|
queueList = new MultiplayerQueueList
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
SelectedItem = { BindTarget = SelectedItem },
|
SelectedItem = { BindTarget = CurrentPlaylistItem },
|
||||||
RequestEdit = item => RequestEdit?.Invoke(item)
|
RequestEdit = item => RequestEdit?.Invoke(item)
|
||||||
},
|
},
|
||||||
historyList = new MultiplayerHistoryList
|
historyList = new MultiplayerHistoryList
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
SelectedItem = { BindTarget = SelectedItem }
|
SelectedItem = { BindTarget = CurrentPlaylistItem }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -449,7 +449,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
switch (client.LocalUser.State)
|
switch (client.LocalUser.State)
|
||||||
{
|
{
|
||||||
case MultiplayerUserState.Spectating:
|
case MultiplayerUserState.Spectating:
|
||||||
return new MultiSpectatorScreen(users.Take(PlayerGrid.MAX_PLAYERS).ToArray());
|
return new MultiSpectatorScreen(Room, users.Take(PlayerGrid.MAX_PLAYERS).ToArray());
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new MultiplayerPlayerLoader(() => new MultiplayerPlayer(Room, SelectedItem.Value, users));
|
return new MultiplayerPlayerLoader(() => new MultiplayerPlayer(Room, SelectedItem.Value, users));
|
||||||
|
@ -11,10 +11,13 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Spectate;
|
using osu.Game.Screens.Spectate;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||||
{
|
{
|
||||||
@ -34,6 +37,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllPlayersLoaded => instances.All(p => p?.PlayerLoaded == true);
|
public bool AllPlayersLoaded => instances.All(p => p?.PlayerLoaded == true);
|
||||||
|
|
||||||
|
protected override UserActivity InitialActivity => new UserActivity.SpectatingMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
@ -48,15 +53,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
private PlayerArea currentAudioSource;
|
private PlayerArea currentAudioSource;
|
||||||
private bool canStartMasterClock;
|
private bool canStartMasterClock;
|
||||||
|
|
||||||
|
private readonly Room room;
|
||||||
private readonly MultiplayerRoomUser[] users;
|
private readonly MultiplayerRoomUser[] users;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="MultiSpectatorScreen"/>.
|
/// Creates a new <see cref="MultiSpectatorScreen"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="room">The room.</param>
|
||||||
/// <param name="users">The players to spectate.</param>
|
/// <param name="users">The players to spectate.</param>
|
||||||
public MultiSpectatorScreen(MultiplayerRoomUser[] users)
|
public MultiSpectatorScreen(Room room, MultiplayerRoomUser[] users)
|
||||||
: base(users.Select(u => u.UserID).ToArray())
|
: base(users.Select(u => u.UserID).ToArray())
|
||||||
{
|
{
|
||||||
|
this.room = room;
|
||||||
this.users = users;
|
this.users = users;
|
||||||
|
|
||||||
instances = new PlayerArea[Users.Count];
|
instances = new PlayerArea[Users.Count];
|
||||||
@ -65,7 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Container leaderboardContainer;
|
FillFlowContainer leaderboardFlow;
|
||||||
Container scoreDisplayContainer;
|
Container scoreDisplayContainer;
|
||||||
|
|
||||||
masterClockContainer = CreateMasterGameplayClockContainer(Beatmap.Value);
|
masterClockContainer = CreateMasterGameplayClockContainer(Beatmap.Value);
|
||||||
@ -97,10 +105,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
leaderboardContainer = new Container
|
leaderboardFlow = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
Anchor = Anchor.CentreLeft,
|
||||||
AutoSizeAxes = Axes.X
|
Origin = Anchor.CentreLeft,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(5)
|
||||||
},
|
},
|
||||||
grid = new PlayerGrid { RelativeSizeAxes = Axes.Both }
|
grid = new PlayerGrid { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
}
|
||||||
@ -125,14 +136,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, users)
|
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, users)
|
||||||
{
|
{
|
||||||
Expanded = { Value = true },
|
Expanded = { Value = true },
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
}, l =>
|
}, l =>
|
||||||
{
|
{
|
||||||
foreach (var instance in instances)
|
foreach (var instance in instances)
|
||||||
leaderboard.AddClock(instance.UserId, instance.GameplayClock);
|
leaderboard.AddClock(instance.UserId, instance.GameplayClock);
|
||||||
|
|
||||||
leaderboardContainer.Add(leaderboard);
|
leaderboardFlow.Insert(0, leaderboard);
|
||||||
|
|
||||||
if (leaderboard.TeamScores.Count == 2)
|
if (leaderboard.TeamScores.Count == 2)
|
||||||
{
|
{
|
||||||
@ -143,6 +152,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
|||||||
}, scoreDisplayContainer.Add);
|
}, scoreDisplayContainer.Add);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
LoadComponentAsync(new GameplayChatDisplay(room)
|
||||||
|
{
|
||||||
|
Expanded = { Value = true },
|
||||||
|
}, chat => leaderboardFlow.Insert(1, chat));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -32,9 +32,22 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
[Resolved(typeof(Room))]
|
[Resolved(typeof(Room))]
|
||||||
protected Bindable<MatchType> Type { get; private set; }
|
protected Bindable<MatchType> Type { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The currently selected item in the <see cref="RoomSubScreen"/>, or the current item from <see cref="Playlist"/>
|
||||||
|
/// if this <see cref="OnlinePlayComposite"/> is not within a <see cref="RoomSubScreen"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Resolved(typeof(Room))]
|
||||||
|
protected Bindable<PlaylistItem> CurrentPlaylistItem { get; private set; }
|
||||||
|
|
||||||
|
[Resolved(typeof(Room))]
|
||||||
|
protected Bindable<Room.RoomPlaylistItemStats> PlaylistItemStats { get; private set; }
|
||||||
|
|
||||||
[Resolved(typeof(Room))]
|
[Resolved(typeof(Room))]
|
||||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||||
|
|
||||||
|
[Resolved(typeof(Room))]
|
||||||
|
protected Bindable<Room.RoomDifficultyRange> DifficultyRange { get; private set; }
|
||||||
|
|
||||||
[Resolved(typeof(Room))]
|
[Resolved(typeof(Room))]
|
||||||
protected Bindable<RoomCategory> Category { get; private set; }
|
protected Bindable<RoomCategory> Category { get; private set; }
|
||||||
|
|
||||||
@ -71,12 +84,6 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IBindable<PlaylistItem> subScreenSelectedItem { get; set; }
|
private IBindable<PlaylistItem> subScreenSelectedItem { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The currently selected item in the <see cref="RoomSubScreen"/>, or the current item from <see cref="Playlist"/>
|
|
||||||
/// if this <see cref="OnlinePlayComposite"/> is not within a <see cref="RoomSubScreen"/>.
|
|
||||||
/// </summary>
|
|
||||||
protected readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -85,9 +92,13 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
Playlist.BindCollectionChanged((_, __) => UpdateSelectedItem(), true);
|
Playlist.BindCollectionChanged((_, __) => UpdateSelectedItem(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdateSelectedItem()
|
protected void UpdateSelectedItem()
|
||||||
=> SelectedItem.Value = RoomID.Value == null || subScreenSelectedItem == null
|
{
|
||||||
? Playlist.GetCurrentItem()
|
// null room ID means this is a room in the process of being created.
|
||||||
: subScreenSelectedItem.Value;
|
if (RoomID.Value == null)
|
||||||
|
CurrentPlaylistItem.Value = Playlist.GetCurrentItem();
|
||||||
|
else if (subScreenSelectedItem != null)
|
||||||
|
CurrentPlaylistItem.Value = subScreenSelectedItem.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.API;
|
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -40,8 +39,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
if (ruleset.Value.OnlineID != PlaylistItem.RulesetID)
|
if (ruleset.Value.OnlineID != PlaylistItem.RulesetID)
|
||||||
throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset");
|
throw new InvalidOperationException("Current Ruleset does not match PlaylistItem's Ruleset");
|
||||||
|
|
||||||
var localMods = Mods.Value.Select(m => new APIMod(m)).ToArray();
|
var requiredLocalMods = PlaylistItem.RequiredMods.Select(m => m.ToMod(GameplayState.Ruleset));
|
||||||
if (!PlaylistItem.RequiredMods.All(m => localMods.Any(m.Equals)))
|
if (!requiredLocalMods.All(m => Mods.Value.Any(m.Equals)))
|
||||||
throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods");
|
throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +43,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
|||||||
|
|
||||||
if (ruleset != null)
|
if (ruleset != null)
|
||||||
{
|
{
|
||||||
|
room.PlaylistItemStats.Value = new Room.RoomPlaylistItemStats
|
||||||
|
{
|
||||||
|
RulesetIDs = new[] { ruleset.OnlineID },
|
||||||
|
};
|
||||||
|
|
||||||
room.Playlist.Add(new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() })
|
room.Playlist.Add(new PlaylistItem(new BeatmapInfo { Metadata = new BeatmapMetadata() })
|
||||||
{
|
{
|
||||||
RulesetID = ruleset.OnlineID,
|
RulesetID = ruleset.OnlineID,
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly);
|
var dllStore = new DllResourceStore(GetType().Assembly);
|
||||||
|
|
||||||
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), this, true);
|
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), this, true);
|
||||||
defaultSkin = new DefaultLegacySkin(this);
|
defaultSkin = new DefaultLegacySkin(this);
|
||||||
|
@ -50,6 +50,16 @@ namespace osu.Game.Users
|
|||||||
public override string Status => $@"{base.Status} with others";
|
public override string Status => $@"{base.Status} with others";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SpectatingMultiplayerGame : InGame
|
||||||
|
{
|
||||||
|
public SpectatingMultiplayerGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset)
|
||||||
|
: base(beatmapInfo, ruleset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Status => $"Watching others {base.Status.ToLowerInvariant()}";
|
||||||
|
}
|
||||||
|
|
||||||
public class InPlaylistGame : InGame
|
public class InPlaylistGame : InGame
|
||||||
{
|
{
|
||||||
public InPlaylistGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset)
|
public InPlaylistGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset)
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.9.0" />
|
<PackageReference Include="Realm" Version="10.9.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.217.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.223.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.14.0" />
|
<PackageReference Include="Sentry" Version="3.14.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.217.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.223.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||||
@ -83,7 +83,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.14" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.14" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.217.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.223.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user