1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-05 10:23:20 +08:00
This commit is contained in:
Sheppsu 2024-12-02 15:48:02 -06:00 committed by GitHub
commit ac75507b28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 152 additions and 41 deletions

View File

@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public partial class TestSceneOsuAnalysisContainer : OsuTestScene
{
private TestReplayAnalysisOverlay analysisContainer = null!;
private ReplayAnalysisSettings settings = null!;
private OsuReplayAnalysisSettings settings = null!;
[Cached]
private OsuRulesetConfigManager config = new OsuRulesetConfigManager(null, new OsuRuleset().RulesetInfo);
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Child = analysisContainer = new TestReplayAnalysisOverlay(fabricateReplay()),
},
settings = new ReplayAnalysisSettings(config),
settings = new TestOsuReplayAnalysisSettings(Ruleset.Value.CreateInstance(), config),
};
settings.ShowClickMarkers.Value = false;
@ -129,5 +129,26 @@ namespace osu.Game.Rulesets.Osu.Tests
public bool AimMarkersVisible => FrameMarkers?.Alpha > 0 && FrameMarkers.Entries.Any();
public bool AimLinesVisible => CursorPath?.Alpha > 0 && CursorPath.Vertices.Count > 1;
}
private partial class TestOsuReplayAnalysisSettings : OsuReplayAnalysisSettings
{
private readonly OsuRulesetConfigManager config;
public TestOsuReplayAnalysisSettings(Ruleset ruleset, OsuRulesetConfigManager config)
: base(ruleset)
{
this.config = config;
}
[BackgroundDependencyLoader]
private void load()
{
config.BindWith(OsuRulesetSetting.ReplayClickMarkersEnabled, ShowClickMarkers);
config.BindWith(OsuRulesetSetting.ReplayFrameMarkersEnabled, ShowAimMarkers);
config.BindWith(OsuRulesetSetting.ReplayCursorPathEnabled, ShowCursorPath);
config.BindWith(OsuRulesetSetting.ReplayCursorHideEnabled, HideSkinCursor);
config.BindWith(OsuRulesetSetting.ReplayAnalysisDisplayLength, DisplayLength);
}
}
}
}

View File

@ -382,5 +382,12 @@ namespace osu.Game.Rulesets.Osu
}
public override bool EditorShowScrollSpeed => false;
public override ReplayAnalysisSettings CreateReplayAnalysisSettings()
{
var settings = new OsuReplayAnalysisSettings(this);
settings.Expanded.Value = false;
return settings;
}
}
}

View File

@ -40,14 +40,13 @@ namespace osu.Game.Rulesets.Osu.UI
}
[BackgroundDependencyLoader]
private void load(ReplayPlayer? replayPlayer)
private void load(Player? player)
{
if (replayPlayer != null)
if (player is ReplayPlayer || player is SpectatorPlayer)
{
ReplayAnalysisOverlay analysisOverlay;
PlayfieldAdjustmentContainer.Add(analysisOverlay = new ReplayAnalysisOverlay(replayPlayer.Score.Replay));
PlayfieldAdjustmentContainer.Add(analysisOverlay = new ReplayAnalysisOverlay(player.Score.Replay));
Overlays.Add(analysisOverlay.CreateProxy().With(p => p.Depth = float.NegativeInfinity));
replayPlayer.AddSettings(new ReplayAnalysisSettings(Config));
cursorHideEnabled = Config.GetBindable<bool>(OsuRulesetSetting.ReplayCursorHideEnabled);

View File

@ -5,13 +5,14 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play.PlayerSettings;
namespace osu.Game.Rulesets.Osu.UI
{
public partial class ReplayAnalysisSettings : PlayerSettingsGroup
public partial class OsuReplayAnalysisSettings : ReplayAnalysisSettings
{
private readonly OsuRulesetConfigManager config;
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
[SettingSource("Show click markers", SettingControlType = typeof(PlayerCheckbox))]
public BindableBool ShowClickMarkers { get; } = new BindableBool();
@ -34,22 +35,19 @@ namespace osu.Game.Rulesets.Osu.UI
Precision = 200,
};
public ReplayAnalysisSettings(OsuRulesetConfigManager config)
: base("Analysis Settings")
public OsuReplayAnalysisSettings(Ruleset ruleset)
: base(ruleset)
{
this.config = config;
}
[BackgroundDependencyLoader]
private void load()
{
AddRange(this.CreateSettingsControls());
config.BindWith(OsuRulesetSetting.ReplayClickMarkersEnabled, ShowClickMarkers);
config.BindWith(OsuRulesetSetting.ReplayFrameMarkersEnabled, ShowAimMarkers);
config.BindWith(OsuRulesetSetting.ReplayCursorPathEnabled, ShowCursorPath);
config.BindWith(OsuRulesetSetting.ReplayCursorHideEnabled, HideSkinCursor);
config.BindWith(OsuRulesetSetting.ReplayAnalysisDisplayLength, DisplayLength);
Config.BindWith(OsuRulesetSetting.ReplayClickMarkersEnabled, ShowClickMarkers);
Config.BindWith(OsuRulesetSetting.ReplayFrameMarkersEnabled, ShowAimMarkers);
Config.BindWith(OsuRulesetSetting.ReplayCursorPathEnabled, ShowCursorPath);
Config.BindWith(OsuRulesetSetting.ReplayCursorHideEnabled, HideSkinCursor);
Config.BindWith(OsuRulesetSetting.ReplayAnalysisDisplayLength, DisplayLength);
}
}
}

View File

@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly Replay replay;
private int replayFrameIndex;
public ReplayAnalysisOverlay(Replay replay)
{
RelativeSizeAxes = Axes.Both;
@ -52,20 +54,39 @@ namespace osu.Game.Rulesets.Osu.UI
displayLength.BindValueChanged(_ =>
{
// Need to fully reload to make this work.
loaded.Invalidate();
invalidateLoaded();
}, true);
}
private readonly Cached loaded = new Cached();
/// <summary>
/// Invalidated when containers are not loaded nor loading, false if loading, and true if loaded
/// </summary>
/// <remarks>
/// Knowing the loading/loaded state is for avoiding an enumeration error when adding
/// new entries and not starting a new load while loading
/// </remarks>
private readonly Cached<bool> loadState = new Cached<bool>();
private CancellationTokenSource? generationCancellationSource;
private void invalidateLoaded()
{
loadState.Invalidate();
replayFrameIndex = 0;
}
protected override void Update()
{
base.Update();
if (requireDisplay)
{
initialise();
// adding entries while the component is asynchronously loading
// can collide with enumeration operations and cause an error
if (loadState.IsValid && loadState.Value)
addEntries();
}
if (ClickMarkers != null) ClickMarkers.Alpha = showClickMarkers.Value ? 1 : 0;
if (FrameMarkers != null) FrameMarkers.Alpha = showFrameMarkers.Value ? 1 : 0;
@ -74,10 +95,10 @@ namespace osu.Game.Rulesets.Osu.UI
private void initialise()
{
if (loaded.IsValid)
if (loadState.IsValid)
return;
loaded.Validate();
loadState.Value = false;
generationCancellationSource?.Cancel();
generationCancellationSource = new CancellationTokenSource();
@ -90,14 +111,23 @@ namespace osu.Game.Rulesets.Osu.UI
FrameMarkers = new FrameMarkerContainer(),
};
LoadComponentsAsync(newDrawables, drawables =>
{
InternalChildrenEnumerable = drawables;
loadState.Value = true;
}, generationCancellationSource.Token);
}
private void addEntries()
{
bool leftHeld = false;
bool rightHeld = false;
// This should probably be async as well, but it's a bit of a pain to debounce and everything.
// Let's address concerns when they are raised.
foreach (var frame in replay.Frames)
while (replayFrameIndex < replay.Frames.Count)
{
var osuFrame = (OsuReplayFrame)frame;
var osuFrame = (OsuReplayFrame)replay.Frames[replayFrameIndex];
bool leftButton = osuFrame.Actions.Contains(OsuAction.LeftButton);
bool rightButton = osuFrame.Actions.Contains(OsuAction.RightButton);
@ -107,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.UI
else if (!leftHeld && leftButton)
{
leftHeld = true;
ClickMarkers.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.LeftButton));
ClickMarkers!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.LeftButton));
}
if (rightHeld && !rightButton)
@ -115,14 +145,14 @@ namespace osu.Game.Rulesets.Osu.UI
else if (!rightHeld && rightButton)
{
rightHeld = true;
ClickMarkers.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.RightButton));
ClickMarkers!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.RightButton));
}
FrameMarkers.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, osuFrame.Actions.ToArray()));
CursorPath.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position));
}
FrameMarkers!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, osuFrame.Actions.ToArray()));
CursorPath!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position));
LoadComponentsAsync(newDrawables, drawables => InternalChildrenEnumerable = drawables, generationCancellationSource.Token);
replayFrameIndex++;
}
}
}
}

View File

@ -426,5 +426,11 @@ namespace osu.Game.Rulesets
/// Can be overridden to avoid showing scroll speed changes in the editor.
/// </summary>
public virtual bool EditorShowScrollSpeed => true;
/// <summary>
/// Creates a ruleset-specific replay analysis settings drawable
/// </summary>
/// <returns>The replay analysis settings drawable</returns>
public virtual ReplayAnalysisSettings? CreateReplayAnalysisSettings() => null;
}
}

View File

@ -0,0 +1,42 @@
// 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.
#nullable disable
using osu.Framework.Allocation;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
using osu.Game.Screens.Play.PlayerSettings;
namespace osu.Game.Rulesets.UI
{
public partial class ReplayAnalysisSettings : PlayerSettingsGroup
{
private readonly Ruleset ruleset;
protected IRulesetConfigManager Config;
public ReplayAnalysisSettings(Ruleset ruleset)
: base("Analysis Settings")
{
this.ruleset = ruleset;
}
[BackgroundDependencyLoader]
private void load()
{
AddRange(this.CreateSettingsControls());
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
Config = dependencies.Get<IRulesetConfigCache>().GetConfigFor(ruleset);
if (Config is not null)
dependencies.Cache(Config);
return dependencies;
}
}
}

View File

@ -54,6 +54,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
private SpectatorSyncManager syncManager = null!;
private PlayerGrid grid = null!;
private MultiSpectatorLeaderboard leaderboard = null!;
private FillFlowContainer leaderboardFlow = null!;
private PlayerArea? currentAudioSource;
private readonly Room room;
@ -76,7 +77,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
[BackgroundDependencyLoader]
private void load()
{
FillFlowContainer leaderboardFlow;
Container scoreDisplayContainer;
InternalChildren = new Drawable[]
@ -157,6 +157,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
{
Expanded = { Value = true },
}, chat => leaderboardFlow.Insert(1, chat));
var replayAnalysisSettings = Ruleset.Value.CreateInstance().CreateReplayAnalysisSettings();
if (replayAnalysisSettings is not null)
leaderboardFlow.Insert(2, replayAnalysisSettings);
}
protected override void LoadComplete()

View File

@ -1296,6 +1296,16 @@ namespace osu.Game.Screens.Play
}
}
/// <summary>
/// Create and add <see cref="ReplayAnalysisSettings"/> to settings overlay.
/// </summary>
protected void AddReplayAnalysisSettings()
{
var replayAnalysisSettings = DrawableRuleset.Ruleset.CreateReplayAnalysisSettings();
if (replayAnalysisSettings is not null)
HUDOverlay.PlayerSettingsOverlay.Add(replayAnalysisSettings);
}
#endregion
IBindable<bool> ISamplePlaybackDisabler.SamplePlaybackDisabled => samplePlaybackDisabled;

View File

@ -55,16 +55,6 @@ namespace osu.Game.Screens.Play
this.createScore = createScore;
}
/// <summary>
/// Add a settings group to the HUD overlay. Intended to be used by rulesets to add replay-specific settings.
/// </summary>
/// <param name="settings">The settings group to be shown.</param>
public void AddSettings(PlayerSettingsGroup settings) => Schedule(() =>
{
settings.Expanded.Value = false;
HUDOverlay.PlayerSettingsOverlay.Add(settings);
});
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
@ -81,6 +71,8 @@ namespace osu.Game.Screens.Play
playbackSettings.UserPlaybackRate.BindTo(master.UserPlaybackRate);
HUDOverlay.PlayerSettingsOverlay.AddAtStart(playbackSettings);
AddReplayAnalysisSettings();
}
protected override void PrepareReplay()

View File

@ -50,6 +50,8 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
});
AddReplayAnalysisSettings();
}
protected override void LoadComplete()