1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-26 19:11:18 +08:00

Merge branch 'master' into mod-overlay/sheared-toggle

This commit is contained in:
Bartłomiej Dach
2022-03-31 22:20:20 +02:00
Unverified
36 changed files with 321 additions and 150 deletions
+2
View File
@@ -0,0 +1,2 @@
# Normalize all the line endings
32a74f95a5c80a0ed18e693f13a47522099df5c3
@@ -3,22 +3,14 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.EmptyFreeform.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.EmptyFreeform.Mods
{
public class EmptyFreeformModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo
{
User = new APIUser { Username = "sample" },
},
Replay = new EmptyFreeformAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new EmptyFreeformAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "sample" });
}
}
@@ -3,22 +3,14 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Pippidon.Replays;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Pippidon.Mods
{
public class PippidonModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo
{
User = new APIUser { Username = "sample" },
},
Replay = new PippidonAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new PippidonAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "sample" });
}
}
@@ -1,24 +1,16 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.EmptyScrolling.Replays;
using osu.Game.Scoring;
using System.Collections.Generic;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.EmptyScrolling.Replays;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.EmptyScrolling.Mods
{
public class EmptyScrollingModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo
{
User = new APIUser { Username = "sample" },
},
Replay = new EmptyScrollingAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new EmptyScrollingAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "sample" });
}
}
@@ -3,22 +3,14 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Pippidon.Replays;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Pippidon.Mods
{
public class PippidonModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo
{
User = new APIUser { Username = "sample" },
},
Replay = new PippidonAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new PippidonAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "sample" });
}
}
@@ -3,19 +3,14 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "osu!salad" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new CatchAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "osu!salad" });
}
}
@@ -3,20 +3,15 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModCinema : ModCinema<CatchHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "osu!salad" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new CatchAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "osu!salad" });
}
}
@@ -3,20 +3,15 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "osu!topus" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), new ModCreatedUser { Username = "osu!topus" });
}
}
@@ -3,21 +3,16 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModCinema : ModCinema<ManiaHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "osu!topus" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), new ModCreatedUser { Username = "osu!topus" });
}
}
@@ -4,7 +4,6 @@
using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Beatmaps;
@@ -13,7 +12,6 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Tests.Visual;
using osuTK;
@@ -67,11 +65,8 @@ namespace osu.Game.Rulesets.Osu.Tests
private class TestAutoMod : OsuModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "Autoplay" } },
Replay = new MissingAutoGenerator(beatmap, mods).Generate()
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new MissingAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
}
private class MissingAutoGenerator : OsuAutoGeneratorBase
+2 -7
View File
@@ -5,10 +5,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -16,10 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAimAssist), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray();
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "Autoplay" } },
Replay = new OsuAutoGenerator(beatmap, mods).Generate()
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
}
}
+2 -7
View File
@@ -5,11 +5,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -17,10 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAimAssist), typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray();
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "Autoplay" } },
Replay = new OsuAutoGenerator(beatmap, mods).Generate()
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new OsuAutoGenerator(beatmap, mods).Generate(), new ModCreatedUser { Username = "Autoplay" });
}
}
@@ -3,19 +3,14 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModAutoplay : ModAutoplay
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "mekkadosu!" } },
Replay = new TaikoAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new TaikoAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "mekkadosu!" });
}
}
@@ -3,20 +3,15 @@
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModCinema : ModCinema<TaikoHitObject>
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new APIUser { Username = "mekkadosu!" } },
Replay = new TaikoAutoGenerator(beatmap).Generate(),
};
public override ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new ModReplayData(new TaikoAutoGenerator(beatmap).Generate(), new ModCreatedUser { Username = "mekkadosu!" });
}
}
@@ -5,12 +5,14 @@ using System.ComponentModel;
using System.Linq;
using osu.Framework.Testing;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.Break;
using osu.Game.Screens.Ranking;
using osu.Game.Users.Drawables;
namespace osu.Game.Tests.Visual.Gameplay
{
@@ -39,11 +41,18 @@ namespace osu.Game.Tests.Visual.Gameplay
seekToBreak(1);
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
AddUntilStep("results displayed", () => getResultsScreen() != null);
AddUntilStep("results displayed", () => getResultsScreen()?.IsLoaded == true);
AddAssert("score has combo", () => getResultsScreen().Score.Combo > 100);
AddAssert("score has no misses", () => getResultsScreen().Score.Statistics[HitResult.Miss] == 0);
AddUntilStep("avatar displayed", () => getAvatar() != null);
AddAssert("avatar not clickable", () => getAvatar().ChildrenOfType<OsuClickableContainer>().First().Action == null);
ClickableAvatar getAvatar() => getResultsScreen()
.ChildrenOfType<ClickableAvatar>().FirstOrDefault();
ResultsScreen getResultsScreen() => Stack.CurrentScreen as ResultsScreen;
}
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty<Mod>());
return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod()?.CreateReplayScore(beatmap, Array.Empty<Mod>()));
return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod()?.CreateScoreFromReplayData(beatmap, Array.Empty<Mod>()));
}
protected override void AddCheckSteps()
@@ -1,16 +1,21 @@
// 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.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Overlays.Settings.Sections.Input;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Settings
{
[TestFixture]
public class TestSceneSettingsPanel : OsuTestScene
public class TestSceneSettingsPanel : OsuManualInputManagerTestScene
{
private SettingsPanel settings;
private DialogOverlay dialogOverlay;
@@ -33,7 +38,55 @@ namespace osu.Game.Tests.Visual.Settings
public void ToggleVisibility()
{
AddWaitStep("wait some", 5);
AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility());
AddToggleStep("toggle visibility", visible => settings.ToggleVisibility());
}
[Test]
public void TestTextboxFocusAfterNestedPanelBackButton()
{
AddUntilStep("sections loaded", () => settings.SectionsContainer.Children.Count > 0);
AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType<FocusedTextBox>().FirstOrDefault()?.HasFocus == true);
AddStep("open key binding subpanel", () =>
{
settings.SectionsContainer
.ChildrenOfType<InputSection>().FirstOrDefault()?
.ChildrenOfType<OsuButton>().FirstOrDefault()?
.TriggerClick();
});
AddUntilStep("binding panel textbox focused", () => settings
.ChildrenOfType<KeyBindingPanel>().FirstOrDefault()?
.ChildrenOfType<FocusedTextBox>().FirstOrDefault()?.HasFocus == true);
AddStep("Press back", () => settings
.ChildrenOfType<KeyBindingPanel>().FirstOrDefault()?
.ChildrenOfType<SettingsSubPanel.BackButton>().FirstOrDefault()?.TriggerClick());
AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType<FocusedTextBox>().FirstOrDefault()?.HasFocus == true);
}
[Test]
public void TestTextboxFocusAfterNestedPanelEscape()
{
AddUntilStep("sections loaded", () => settings.SectionsContainer.Children.Count > 0);
AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType<FocusedTextBox>().FirstOrDefault()?.HasFocus == true);
AddStep("open key binding subpanel", () =>
{
settings.SectionsContainer
.ChildrenOfType<InputSection>().FirstOrDefault()?
.ChildrenOfType<OsuButton>().FirstOrDefault()?
.TriggerClick();
});
AddUntilStep("binding panel textbox focused", () => settings
.ChildrenOfType<KeyBindingPanel>().FirstOrDefault()?
.ChildrenOfType<FocusedTextBox>().FirstOrDefault()?.HasFocus == true);
AddStep("Escape", () => InputManager.Key(Key.Escape));
AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType<FocusedTextBox>().FirstOrDefault()?.HasFocus == true);
}
[BackgroundDependencyLoader]
@@ -1,9 +1,13 @@
// 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.IO;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Game.Rulesets;
using osu.Game.Tests;
@@ -12,6 +16,45 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
public class DataLoadTest : TournamentHostTest
{
[Test]
public void TestRulesetGetsValidOnlineID()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
{
try
{
var osu = new TestTournament(runOnLoadComplete: () =>
{
// ReSharper disable once AccessToDisposedClosure
var storage = host.Storage.GetStorageForDirectory(Path.Combine("tournaments", "default"));
using (var stream = storage.GetStream("bracket.json", FileAccess.Write, FileMode.Create))
using (var writer = new StreamWriter(stream))
{
writer.Write(@"{
""Ruleset"": {
""ShortName"": ""taiko"",
""OnlineID"": -1,
""Name"": ""osu!taiko"",
""InstantiationInfo"": ""osu.Game.Rulesets.OsuTaiko.TaikoRuleset, osu.Game.Rulesets.Taiko"",
""Available"": true
} }");
}
});
LoadTournament(host, osu);
osu.BracketLoadTask.WaitSafely();
Assert.That(osu.Dependencies.Get<IBindable<RulesetInfo>>().Value.OnlineID, Is.EqualTo(1));
}
finally
{
host.Exit();
}
}
}
[Test]
public void TestUnavailableRuleset()
{
@@ -19,7 +62,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
try
{
var osu = new TestTournament();
var osu = new TestTournament(true);
LoadTournament(host, osu);
var storage = osu.Dependencies.Get<Storage>();
@@ -35,10 +78,23 @@ namespace osu.Game.Tournament.Tests.NonVisual
public class TestTournament : TournamentGameBase
{
private readonly bool resetRuleset;
private readonly Action runOnLoadComplete;
public new Task BracketLoadTask => base.BracketLoadTask;
public TestTournament(bool resetRuleset = false, Action runOnLoadComplete = null)
{
this.resetRuleset = resetRuleset;
this.runOnLoadComplete = runOnLoadComplete;
}
protected override void LoadComplete()
{
runOnLoadComplete?.Invoke();
base.LoadComplete();
Ruleset.Value = new RulesetInfo(); // not available
if (resetRuleset)
Ruleset.Value = new RulesetInfo(); // not available
}
}
}
+15 -11
View File
@@ -64,6 +64,16 @@ namespace osu.Game.Tournament
Textures.AddStore(new TextureLoaderStore(new StorageBackedResourceStore(storage)));
dependencies.CacheAs(new StableInfo(storage));
}
protected override void LoadComplete()
{
MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display
// we don't want to show the menu cursor as it would appear on stream output.
MenuCursorContainer.Cursor.Alpha = 0;
base.LoadComplete();
Task.Run(readBracket);
}
@@ -81,10 +91,14 @@ namespace osu.Game.Tournament
ladder ??= new LadderInfo();
ladder.Ruleset.Value = ladder.Ruleset.Value != null
var resolvedRuleset = ladder.Ruleset.Value != null
? RulesetStore.GetRuleset(ladder.Ruleset.Value.ShortName)
: RulesetStore.AvailableRulesets.First();
// Must set to null initially to avoid the following re-fetch hitting `ShortName` based equality check.
ladder.Ruleset.Value = null;
ladder.Ruleset.Value = resolvedRuleset;
bool addedInfo = false;
// assign teams
@@ -282,16 +296,6 @@ namespace osu.Game.Tournament
}
}
protected override void LoadComplete()
{
MenuCursorContainer.Cursor.AlwaysPresent = true; // required for tooltip display
// we don't want to show the menu cursor as it would appear on stream output.
MenuCursorContainer.Cursor.Alpha = 0;
base.LoadComplete();
}
protected virtual void SaveChanges()
{
if (!bracketLoadTaskCompletionSource.Task.IsCompletedSuccessfully)
@@ -28,7 +28,7 @@ namespace osu.Game.Graphics.UserInterface
if (!allowImmediateFocus)
return;
Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this), false);
Scheduler.Add(() => GetContainingInputManager().ChangeFocus(this));
}
public new void KillFocus() => base.KillFocus();
@@ -15,6 +15,11 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonObject(MemberSerialization.OptIn)]
public class APIUser : IEquatable<APIUser>, IUser
{
/// <summary>
/// A user ID which can be used to represent any system user which is not attached to a user profile.
/// </summary>
public const int SYSTEM_USER_ID = 0;
[JsonProperty(@"id")]
public int Id { get; set; } = 1;
@@ -238,7 +243,7 @@ namespace osu.Game.Online.API.Requests.Responses
/// </summary>
public static readonly APIUser SYSTEM_USER = new APIUser
{
Id = 0,
Id = SYSTEM_USER_ID,
Username = "system",
Colour = @"9c0101",
};
+1 -1
View File
@@ -32,7 +32,7 @@ namespace osu.Game.Overlays
protected override bool DimMainContent => false; // dimming is handled by main overlay
private class BackButton : SidebarButton
public class BackButton : SidebarButton
{
private Container content;
+1 -1
View File
@@ -41,7 +41,7 @@ namespace osu.Game.Overlays
public void ShowUser(IUser user)
{
if (user == APIUser.SYSTEM_USER)
if (user.OnlineID == APIUser.SYSTEM_USER_ID)
return;
Show();
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Edit
private void regenerateAutoplay()
{
var autoplayMod = drawableRuleset.Mods.OfType<ModAutoplay>().Single();
drawableRuleset.SetReplayScore(autoplayMod.CreateReplayScore(drawableRuleset.Beatmap, drawableRuleset.Mods));
drawableRuleset.SetReplayScore(autoplayMod.CreateScoreFromReplayData(drawableRuleset.Beatmap, drawableRuleset.Mods));
}
private void addHitObject(HitObject hitObject)
+9 -1
View File
@@ -1,14 +1,22 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
public interface ICreateReplay
[Obsolete("Use ICreateReplayData instead")] // Can be removed 20220929
public interface ICreateReplay : ICreateReplayData
{
public Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods);
ModReplayData ICreateReplayData.CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
{
var replayScore = CreateReplayScore(beatmap, mods);
return new ModReplayData(replayScore.Replay, new ModCreatedUser { Username = replayScore.ScoreInfo.User.Username });
}
}
}
@@ -0,0 +1,63 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Replays;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// A mod which creates full replay data, which is to be played back in place of a local user playing the game.
/// </summary>
public interface ICreateReplayData
{
/// <summary>
/// Create replay data.
/// </summary>
/// <param name="beatmap">The beatmap to create replay data for.</param>
/// <param name="mods">The mods to take into account when creating the replay data.</param>
/// <returns>A <see cref="ModReplayData"/> structure, containing the generated replay data.</returns>
/// <remarks>
/// For callers that want to receive a directly usable <see cref="Score"/> instance,
/// the <see cref="ModExtensions.CreateScoreFromReplayData"/> extension method is provided for convenience.
/// </remarks>
ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods);
}
/// <summary>
/// Data created by a mod that implements <see cref="ICreateReplayData"/>.
/// </summary>
public class ModReplayData
{
/// <summary>
/// The full replay data.
/// </summary>
public readonly Replay Replay;
/// <summary>
/// Placeholder user data to show in place of the local user when the associated mod is active.
/// </summary>
public readonly ModCreatedUser User;
public ModReplayData(Replay replay, ModCreatedUser user = null)
{
Replay = replay;
User = user ?? new ModCreatedUser();
}
}
/// <summary>
/// A user which is associated with a replay that was created by a mod (ie. autoplay or cinema).
/// </summary>
public class ModCreatedUser : IUser
{
public int OnlineID => APIUser.SYSTEM_USER_ID;
public bool IsBot => true;
public string Username { get; set; } = string.Empty;
}
}
+11 -1
View File
@@ -11,7 +11,7 @@ using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModAutoplay : Mod, IApplicableFailOverride, ICreateReplay
public abstract class ModAutoplay : Mod, IApplicableFailOverride, ICreateReplayData
{
public override string Name => "Autoplay";
public override string Acronym => "AT";
@@ -30,6 +30,16 @@ namespace osu.Game.Rulesets.Mods
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
[Obsolete("Override CreateReplayData(IBeatmap, IReadOnlyList<Mod>) instead")] // Can be removed 20220929
public virtual Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score { Replay = new Replay() };
public virtual ModReplayData CreateReplayData(IBeatmap beatmap, IReadOnlyList<Mod> mods)
{
#pragma warning disable CS0618
var replayScore = CreateReplayScore(beatmap, mods);
#pragma warning restore CS0618
return new ModReplayData(replayScore.Replay, new ModCreatedUser { Username = replayScore.ScoreInfo.User.Username });
}
}
}
+31
View File
@@ -0,0 +1,31 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
public static class ModExtensions
{
public static Score CreateScoreFromReplayData(this ICreateReplayData mod, IBeatmap beatmap, IReadOnlyList<Mod> mods)
{
var replayData = mod.CreateReplayData(beatmap, mods);
return new Score
{
Replay = replayData.Replay,
ScoreInfo =
{
User = new APIUser
{
Id = APIUser.SYSTEM_USER_ID,
Username = replayData.User.Username,
}
}
};
}
}
}
+3
View File
@@ -207,6 +207,9 @@ namespace osu.Game.Screens.Menu
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Repeat || e.ControlPressed || e.ShiftPressed || e.AltPressed || e.SuperPressed)
return false;
if (State == ButtonSystemState.Initial)
{
if (buttonsTopLevel.Any(b => e.Key == b.TriggerKey))
@@ -37,21 +37,21 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
base.UserJoined(user);
userJoinedSample?.Play();
Scheduler.AddOnce(() => userJoinedSample?.Play());
}
protected override void UserLeft(MultiplayerRoomUser user)
{
base.UserLeft(user);
userLeftSample?.Play();
Scheduler.AddOnce(() => userLeftSample?.Play());
}
protected override void UserKicked(MultiplayerRoomUser user)
{
base.UserKicked(user);
userKickedSample?.Play();
Scheduler.AddOnce(() => userKickedSample?.Play());
}
private void hostChanged(ValueChangedEvent<APIUser> value)
@@ -59,7 +59,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
// only play sound when the host changes from an already-existing host.
if (value.OldValue == null) return;
hostChangedSample?.Play();
Scheduler.AddOnce(() => hostChangedSample?.Play());
}
}
}
@@ -227,15 +227,15 @@ namespace osu.Game.Screens.Play.HUD
public int? Team => (User.MatchState as TeamVersusUserState)?.TeamID;
private readonly RulesetInfo ruleset;
private readonly ScoreInfo scoreInfo;
public TrackedUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor)
{
this.ruleset = ruleset;
User = user;
ScoreProcessor = scoreProcessor;
scoreInfo = new ScoreInfo { Ruleset = ruleset };
ScoringMode.BindValueChanged(_ => UpdateScore());
}
@@ -253,12 +253,10 @@ namespace osu.Game.Screens.Play.HUD
{
var header = frame.Header;
Score.Value = ScoreProcessor.ComputePartialScore(ScoringMode.Value, new ScoreInfo
{
Ruleset = ruleset,
MaxCombo = header.MaxCombo,
Statistics = header.Statistics
});
scoreInfo.MaxCombo = header.MaxCombo;
scoreInfo.Statistics = header.Statistics;
Score.Value = ScoreProcessor.ComputePartialScore(ScoringMode.Value, scoreInfo);
Accuracy.Value = header.Accuracy;
CurrentCombo.Value = header.Combo;
+5 -2
View File
@@ -98,9 +98,12 @@ namespace osu.Game.Screens.Select
Player createPlayer()
{
var replayGeneratingMod = Mods.Value.OfType<ICreateReplay>().FirstOrDefault();
var replayGeneratingMod = Mods.Value.OfType<ICreateReplayData>().FirstOrDefault();
if (replayGeneratingMod != null)
return new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateReplayScore(beatmap, mods));
{
return new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods));
}
return new SoloPlayer();
}
@@ -14,6 +14,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osuTK;
@@ -94,7 +95,7 @@ namespace osu.Game.Skinning.Editor
var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod();
if (replayGeneratingMod != null)
screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateReplayScore(beatmap, mods))));
screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods))));
}, new[] { typeof(Player), typeof(SongSelect) })
},
}
+1 -1
View File
@@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual
if (autoplayMod != null)
{
DrawableRuleset?.SetReplayScore(autoplayMod.CreateReplayScore(GameplayState.Beatmap, Mods.Value));
DrawableRuleset?.SetReplayScore(autoplayMod.CreateScoreFromReplayData(GameplayState.Beatmap, Mods.Value));
return;
}
+1 -1
View File
@@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual
/// Instantiate a replay player that renders an autoplay mod.
/// </summary>
public TestReplayPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
: base((beatmap, mods) => mods.OfType<ModAutoplay>().First().CreateReplayScore(beatmap, mods), new PlayerConfiguration
: base((beatmap, mods) => mods.OfType<ModAutoplay>().First().CreateScoreFromReplayData(beatmap, mods), new PlayerConfiguration
{
AllowPause = allowPause,
ShowResults = showResults
+4 -2
View File
@@ -21,7 +21,7 @@ namespace osu.Game.Users.Drawables
/// </summary>
public bool OpenOnClick
{
set => clickableArea.Enabled.Value = value;
set => clickableArea.Enabled.Value = clickableArea.Action != null && value;
}
/// <summary>
@@ -52,8 +52,10 @@ namespace osu.Game.Users.Drawables
Add(clickableArea = new ClickableArea
{
RelativeSizeAxes = Axes.Both,
Action = openProfile
});
if (user?.Id != APIUser.SYSTEM_USER_ID)
clickableArea.Action = openProfile;
}
[BackgroundDependencyLoader]