1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-04 04:02:59 +08:00

Merge branch 'master' into update-framework

This commit is contained in:
Dean Herbert 2019-03-18 11:34:59 +09:00 committed by GitHub
commit 392a0a1d74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 297 additions and 198 deletions

View File

@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual
} }
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()

View File

@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuGameBase osu, APIAccess api, RulesetStore rulesets) private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets)
{ {
Bindable<BeatmapInfo> beatmapBindable = new Bindable<BeatmapInfo>(); Bindable<BeatmapInfo> beatmapBindable = new Bindable<BeatmapInfo>();

View File

@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual
public class TestCaseUserProfile : OsuTestCase public class TestCaseUserProfile : OsuTestCase
{ {
private readonly TestUserProfileOverlay profile; private readonly TestUserProfileOverlay profile;
private APIAccess api; private IAPIProvider api;
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;
} }

View File

@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps
private readonly BeatmapStore beatmaps; private readonly BeatmapStore beatmaps;
private readonly APIAccess api; private readonly IAPIProvider api;
private readonly AudioManager audioManager; private readonly AudioManager audioManager;
@ -72,7 +72,7 @@ namespace osu.Game.Beatmaps
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>(); private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, GameHost host = null, public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
WorkingBeatmap defaultBeatmap = null) WorkingBeatmap defaultBeatmap = null)
: base(storage, contextFactory, new BeatmapStore(contextFactory), host) : base(storage, contextFactory, new BeatmapStore(contextFactory), host)
{ {

View File

@ -22,7 +22,7 @@ namespace osu.Game.Online.API
private readonly OsuConfigManager config; private readonly OsuConfigManager config;
private readonly OAuth authentication; private readonly OAuth authentication;
public string Endpoint = @"https://osu.ppy.sh"; public string Endpoint => @"https://osu.ppy.sh";
private const string client_id = @"5"; private const string client_id = @"5";
private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";

View File

@ -61,9 +61,12 @@ namespace osu.Game.Online.API
private Action pendingFailure; private Action pendingFailure;
public void Perform(APIAccess api) public void Perform(IAPIProvider api)
{ {
API = api; if (!(api is APIAccess apiAccess))
throw new NotSupportedException($"A {nameof(APIAccess)} is required to perform requests.");
API = apiAccess;
if (checkAndScheduleFailure()) if (checkAndScheduleFailure())
return; return;
@ -71,7 +74,7 @@ namespace osu.Game.Online.API
WebRequest = CreateWebRequest(); WebRequest = CreateWebRequest();
WebRequest.Failed += Fail; WebRequest.Failed += Fail;
WebRequest.AllowRetryOnTimeout = false; WebRequest.AllowRetryOnTimeout = false;
WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); WebRequest.AddHeader("Authorization", $"Bearer {API.AccessToken}");
if (checkAndScheduleFailure()) if (checkAndScheduleFailure())
return; return;
@ -85,7 +88,7 @@ namespace osu.Game.Online.API
if (checkAndScheduleFailure()) if (checkAndScheduleFailure())
return; return;
api.Schedule(delegate { Success?.Invoke(); }); API.Schedule(delegate { Success?.Invoke(); });
} }
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));

View File

@ -11,14 +11,16 @@ namespace osu.Game.Online.API
public Bindable<User> LocalUser { get; } = new Bindable<User>(new User public Bindable<User> LocalUser { get; } = new Bindable<User>(new User
{ {
Username = @"Dummy", Username = @"Dummy",
Id = 1, Id = 1001,
}); });
public bool IsLoggedIn => true; public bool IsLoggedIn => true;
public void Update() public string ProvidedUsername => LocalUser.Value.Username;
{
} public string Endpoint => "http://localhost";
public APIState State => LocalUser.Value.Id == 1 ? APIState.Offline : APIState.Online;
public virtual void Queue(APIRequest request) public virtual void Queue(APIRequest request)
{ {
@ -26,6 +28,28 @@ namespace osu.Game.Online.API
public void Register(IOnlineComponent component) public void Register(IOnlineComponent component)
{ {
// todo: add support
} }
public void Unregister(IOnlineComponent component)
{
// todo: add support
}
public void Login(string username, string password)
{
LocalUser.Value = new User
{
Username = @"Dummy",
Id = 1001,
};
}
public void Logout()
{
LocalUser.Value = new GuestUser();
}
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password) => null;
} }
} }

View File

@ -18,6 +18,19 @@ namespace osu.Game.Online.API
/// </summary> /// </summary>
bool IsLoggedIn { get; } bool IsLoggedIn { get; }
/// <summary>
/// The last username provided by the end-user.
/// May not be authenticated.
/// </summary>
string ProvidedUsername { get; }
/// <summary>
/// The URL endpoint for this API. Does not include a trailing slash.
/// </summary>
string Endpoint { get; }
APIState State { get; }
/// <summary> /// <summary>
/// Queue a new request. /// Queue a new request.
/// </summary> /// </summary>
@ -29,5 +42,32 @@ namespace osu.Game.Online.API
/// </summary> /// </summary>
/// <param name="component">The component to register.</param> /// <param name="component">The component to register.</param>
void Register(IOnlineComponent component); void Register(IOnlineComponent component);
/// <summary>
/// Unregisters a component to receive state changes.
/// </summary>
/// <param name="component">The component to unregister.</param>
void Unregister(IOnlineComponent component);
/// <summary>
/// Attempt to login using the provided credentials. This is a non-blocking operation.
/// </summary>
/// <param name="username">The user's username.</param>
/// <param name="password">The user's password.</param>
void Login(string username, string password);
/// <summary>
/// Log out the current user.
/// </summary>
void Logout();
/// <summary>
/// Create a new user account. This is a blocking operation.
/// </summary>
/// <param name="email">The email to create the account with.</param>
/// <param name="username">The username to create the account with.</param>
/// <param name="password">The password to create the account with.</param>
/// <returns>Any errors encoutnered during account creation.</returns>
RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password);
} }
} }

View File

@ -5,6 +5,6 @@ namespace osu.Game.Online.API
{ {
public interface IOnlineComponent public interface IOnlineComponent
{ {
void APIStateChanged(APIAccess api, APIState state); void APIStateChanged(IAPIProvider api, APIState state);
} }
} }

View File

@ -174,12 +174,12 @@ namespace osu.Game.Online.Leaderboards
}; };
} }
private APIAccess api; private IAPIProvider api;
private ScheduledDelegate pendingUpdateScores; private ScheduledDelegate pendingUpdateScores;
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;
api?.Register(this); api?.Register(this);
@ -195,7 +195,7 @@ namespace osu.Game.Online.Leaderboards
private APIRequest getScoresRequest; private APIRequest getScoresRequest;
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(IAPIProvider api, APIState state)
{ {
if (state == APIState.Online) if (state == APIState.Online)
UpdateScores(); UpdateScores();

View File

@ -152,7 +152,6 @@ namespace osu.Game
API = new APIAccess(LocalConfig); API = new APIAccess(LocalConfig);
dependencies.Cache(API);
dependencies.CacheAs<IAPIProvider>(API); dependencies.CacheAs<IAPIProvider>(API);
var defaultBeatmap = new DummyWorkingBeatmap(this); var defaultBeatmap = new DummyWorkingBeatmap(this);

View File

@ -33,7 +33,7 @@ namespace osu.Game.Overlays.AccountCreation
private OsuTextBox emailTextBox; private OsuTextBox emailTextBox;
private OsuPasswordTextBox passwordTextBox; private OsuPasswordTextBox passwordTextBox;
private APIAccess api; private IAPIProvider api;
private ShakeContainer registerShake; private ShakeContainer registerShake;
private IEnumerable<Drawable> characterCheckText; private IEnumerable<Drawable> characterCheckText;
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.AccountCreation
private GameHost host; private GameHost host;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api, GameHost host) private void load(OsuColour colours, IAPIProvider api, GameHost host)
{ {
this.api = api; this.api = api;
this.host = host; this.host = host;

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays.AccountCreation
{ {
private OsuTextFlowContainer multiAccountExplanationText; private OsuTextFlowContainer multiAccountExplanationText;
private LinkFlowContainer furtherAssistance; private LinkFlowContainer furtherAssistance;
private APIAccess api; private IAPIProvider api;
private const string help_centre_url = "/help/wiki/Help_Centre#login"; private const string help_centre_url = "/help/wiki/Help_Centre#login";
@ -39,7 +39,7 @@ namespace osu.Game.Overlays.AccountCreation
} }
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(OsuColour colours, APIAccess api, OsuGame game, TextureStore textures) private void load(OsuColour colours, IAPIProvider api, OsuGame game, TextureStore textures)
{ {
this.api = api; this.api = api;

View File

@ -30,7 +30,7 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api) private void load(OsuColour colours, IAPIProvider api)
{ {
api.Register(this); api.Register(this);
@ -96,7 +96,7 @@ namespace osu.Game.Overlays
this.FadeOut(100); this.FadeOut(100);
} }
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(IAPIProvider api, APIState state)
{ {
switch (state) switch (state)
{ {

View File

@ -39,7 +39,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api, BeatmapManager beatmaps) private void load(IAPIProvider api, BeatmapManager beatmaps)
{ {
FillFlowContainer textSprites; FillFlowContainer textSprites;

View File

@ -45,7 +45,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
} }
private GetScoresRequest getScoresRequest; private GetScoresRequest getScoresRequest;
private APIAccess api; private IAPIProvider api;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
@ -129,7 +129,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;
updateDisplay(); updateDisplay();

View File

@ -31,7 +31,7 @@ namespace osu.Game.Overlays
private readonly Header header; private readonly Header header;
private APIAccess api; private IAPIProvider api;
private RulesetStore rulesets; private RulesetStore rulesets;
private readonly ScrollContainer scroll; private readonly ScrollContainer scroll;
@ -101,7 +101,7 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets) private void load(IAPIProvider api, RulesetStore rulesets)
{ {
this.api = api; this.api = api;
this.rulesets = rulesets; this.rulesets = rulesets;

View File

@ -28,7 +28,7 @@ namespace osu.Game.Overlays
{ {
private const float panel_padding = 10f; private const float panel_padding = 10f;
private APIAccess api; private IAPIProvider api;
private RulesetStore rulesets; private RulesetStore rulesets;
private readonly FillFlowContainer resultCountsContainer; private readonly FillFlowContainer resultCountsContainer;
@ -164,7 +164,7 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, PreviewTrackManager previewTrackManager) private void load(OsuColour colours, IAPIProvider api, RulesetStore rulesets, PreviewTrackManager previewTrackManager)
{ {
this.api = api; this.api = api;
this.rulesets = rulesets; this.rulesets = rulesets;

View File

@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections
protected readonly Bindable<User> User = new Bindable<User>(); protected readonly Bindable<User> User = new Bindable<User>();
protected APIAccess Api; protected IAPIProvider Api;
protected APIRequest RetrievalRequest; protected APIRequest RetrievalRequest;
protected RulesetStore Rulesets; protected RulesetStore Rulesets;
@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Profile.Sections
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets) private void load(IAPIProvider api, RulesetStore rulesets)
{ {
Api = api; Api = api;
Rulesets = rulesets; Rulesets = rulesets;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
{ {
public class DrawableRecentActivity : DrawableProfileRow public class DrawableRecentActivity : DrawableProfileRow
{ {
private APIAccess api; private IAPIProvider api;
private readonly APIRecentActivity activity; private readonly APIRecentActivity activity;
@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;

View File

@ -58,14 +58,14 @@ namespace osu.Game.Overlays.Settings.Sections.General
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, APIAccess api) private void load(OsuColour colours, IAPIProvider api)
{ {
this.colours = colours; this.colours = colours;
api?.Register(this); api?.Register(this);
} }
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(IAPIProvider api, APIState state)
{ {
form = null; form = null;
@ -194,7 +194,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
{ {
private TextBox username; private TextBox username;
private TextBox password; private TextBox password;
private APIAccess api; private IAPIProvider api;
public Action RequestHide; public Action RequestHide;
@ -205,7 +205,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
} }
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, OsuConfigManager config, AccountCreationOverlay accountCreation) private void load(IAPIProvider api, OsuConfigManager config, AccountCreationOverlay accountCreation)
{ {
this.api = api; this.api = api;
Direction = FillDirection.Vertical; Direction = FillDirection.Vertical;

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays
{ {
public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>, IOnlineComponent public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>, IOnlineComponent
{ {
private APIAccess api; private IAPIProvider api;
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private FillFlowContainer<SocialPanel> panels; private FillFlowContainer<SocialPanel> panels;
@ -89,7 +89,7 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;
api.Register(this); api.Register(this);
@ -193,7 +193,7 @@ namespace osu.Game.Overlays
} }
} }
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(IAPIProvider api, APIState state)
{ {
switch (state) switch (state)
{ {

View File

@ -43,12 +43,12 @@ namespace osu.Game.Overlays.Toolbar
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
api.Register(this); api.Register(this);
} }
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(IAPIProvider api, APIState state)
{ {
switch (state) switch (state)
{ {

View File

@ -26,7 +26,7 @@ namespace osu.Game.Overlays
private ProfileSection lastSection; private ProfileSection lastSection;
private ProfileSection[] sections; private ProfileSection[] sections;
private GetUserRequest userReq; private GetUserRequest userReq;
private APIAccess api; private IAPIProvider api;
protected ProfileHeader Header; protected ProfileHeader Header;
private SectionsContainer<ProfileSection> sectionsContainer; private SectionsContainer<ProfileSection> sectionsContainer;
private ProfileTabControl tabs; private ProfileTabControl tabs;
@ -56,7 +56,7 @@ namespace osu.Game.Overlays
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;
} }

View File

@ -0,0 +1,153 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Input.Handlers;
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.UI
{
/// <summary>
/// A container which consumes a parent gameplay clock and standardises frame counts for children.
/// Will ensure a minimum of 40 frames per clock second is maintained, regardless of any system lag or seeks.
/// </summary>
public class FrameStabilityContainer : Container, IHasReplayHandler
{
public FrameStabilityContainer()
{
RelativeSizeAxes = Axes.Both;
gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
}
private readonly ManualClock manualClock;
private readonly FramedClock framedClock;
[Cached]
private GameplayClock gameplayClock;
private IFrameBasedClock parentGameplayClock;
[BackgroundDependencyLoader(true)]
private void load(GameplayClock clock)
{
if (clock != null)
parentGameplayClock = clock;
}
protected override void LoadComplete()
{
base.LoadComplete();
setClock();
}
/// <summary>
/// Whether we are running up-to-date with our parent clock.
/// If not, we will need to keep processing children until we catch up.
/// </summary>
private bool requireMoreUpdateLoops;
/// <summary>
/// Whether we are in a valid state (ie. should we keep processing children frames).
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
/// </summary>
private bool validState;
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
private bool isAttached => ReplayInputHandler != null;
private const int max_catch_up_updates_per_frame = 50;
private const double sixty_frame_time = 1000.0 / 60;
public override bool UpdateSubTree()
{
requireMoreUpdateLoops = true;
validState = true;
int loops = 0;
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
{
updateClock();
if (validState)
{
base.UpdateSubTree();
UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat);
}
}
return true;
}
private void updateClock()
{
if (parentGameplayClock == null)
setClock(); // LoadComplete may not be run yet, but we still want the clock.
validState = true;
manualClock.Rate = parentGameplayClock.Rate;
manualClock.IsRunning = parentGameplayClock.IsRunning;
var newProposedTime = parentGameplayClock.CurrentTime;
try
{
if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
{
newProposedTime = manualClock.Rate > 0
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
}
if (!isAttached)
{
manualClock.CurrentTime = newProposedTime;
}
else
{
double? newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime);
if (newTime == null)
{
// we shouldn't execute for this time value. probably waiting on more replay data.
validState = false;
requireMoreUpdateLoops = true;
manualClock.CurrentTime = newProposedTime;
return;
}
manualClock.CurrentTime = newTime.Value;
}
requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime;
}
finally
{
// The manual clock time has changed in the above code. The framed clock now needs to be updated
// to ensure that the its time is valid for our children before input is processed
framedClock.ProcessFrame();
}
}
private void setClock()
{
// in case a parent gameplay clock isn't available, just use the parent clock.
if (parentGameplayClock == null)
parentGameplayClock = Clock;
Clock = gameplayClock;
ProcessCustomClock = false;
}
public ReplayInputHandler ReplayInputHandler { get; set; }
}
}

View File

@ -132,6 +132,8 @@ namespace osu.Game.Rulesets.UI
protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null; protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null;
protected FrameStabilityContainer FrameStabilityContainer;
public Score ReplayScore { get; private set; } public Score ReplayScore { get; private set; }
/// <summary> /// <summary>
@ -149,7 +151,11 @@ namespace osu.Game.Rulesets.UI
throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available"); throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available");
ReplayScore = replayScore; ReplayScore = replayScore;
ReplayInputManager.ReplayInputHandler = replayScore != null ? CreateReplayInputHandler(replayScore.Replay) : null;
var handler = replayScore != null ? CreateReplayInputHandler(replayScore.Replay) : null;
ReplayInputManager.ReplayInputHandler = handler;
FrameStabilityContainer.ReplayInputHandler = handler;
HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null; HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null;
} }
@ -243,7 +249,6 @@ namespace osu.Game.Rulesets.UI
Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo); Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
applyBeatmapMods(Mods); applyBeatmapMods(Mods);
} }
@ -262,7 +267,10 @@ namespace osu.Game.Rulesets.UI
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
KeyBindingInputManager, FrameStabilityContainer = new FrameStabilityContainer
{
Child = KeyBindingInputManager,
},
Overlays = new Container { RelativeSizeAxes = Axes.Both } Overlays = new Container { RelativeSizeAxes = Axes.Both }
}; };

View File

@ -1,7 +1,6 @@
// 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;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -12,7 +11,6 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States; using osu.Framework.Input.States;
using osu.Framework.Timing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
@ -41,7 +39,12 @@ namespace osu.Game.Rulesets.UI
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
{ {
InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique); InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique);
gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); }
[BackgroundDependencyLoader(true)]
private void load(OsuConfigManager config)
{
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
} }
#region Action mapping (for replays) #region Action mapping (for replays)
@ -85,137 +88,6 @@ namespace osu.Game.Rulesets.UI
#endregion #endregion
#region Clock control
private readonly ManualClock manualClock;
private readonly FramedClock framedClock;
[Cached]
private GameplayClock gameplayClock;
private IFrameBasedClock parentGameplayClock;
[BackgroundDependencyLoader(true)]
private void load(OsuConfigManager config, GameplayClock clock)
{
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
if (clock != null)
parentGameplayClock = clock;
}
protected override void LoadComplete()
{
base.LoadComplete();
setClock();
}
/// <summary>
/// Whether we are running up-to-date with our parent clock.
/// If not, we will need to keep processing children until we catch up.
/// </summary>
private bool requireMoreUpdateLoops;
/// <summary>
/// Whether we are in a valid state (ie. should we keep processing children frames).
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
/// </summary>
private bool validState;
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
private bool isAttached => replayInputHandler != null && !UseParentInput;
private const int max_catch_up_updates_per_frame = 50;
private const double sixty_frame_time = 1000.0 / 60;
public override bool UpdateSubTree()
{
requireMoreUpdateLoops = true;
validState = true;
int loops = 0;
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
{
updateClock();
if (validState)
{
base.UpdateSubTree();
UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat);
}
}
return true;
}
private void updateClock()
{
if (parentGameplayClock == null)
setClock(); // LoadComplete may not be run yet, but we still want the clock.
validState = true;
manualClock.Rate = parentGameplayClock.Rate;
manualClock.IsRunning = parentGameplayClock.IsRunning;
var newProposedTime = parentGameplayClock.CurrentTime;
try
{
if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
{
newProposedTime = manualClock.Rate > 0
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
}
if (!isAttached)
{
manualClock.CurrentTime = newProposedTime;
}
else
{
double? newTime = replayInputHandler.SetFrameFromTime(newProposedTime);
if (newTime == null)
{
// we shouldn't execute for this time value. probably waiting on more replay data.
validState = false;
requireMoreUpdateLoops = true;
manualClock.CurrentTime = newProposedTime;
return;
}
manualClock.CurrentTime = newTime.Value;
}
requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime;
}
finally
{
// The manual clock time has changed in the above code. The framed clock now needs to be updated
// to ensure that the its time is valid for our children before input is processed
framedClock.ProcessFrame();
}
}
private void setClock()
{
// in case a parent gameplay clock isn't available, just use the parent clock.
if (parentGameplayClock == null)
parentGameplayClock = Clock;
Clock = gameplayClock;
ProcessCustomClock = false;
}
#endregion
#region Setting application (disables etc.) #region Setting application (disables etc.)
private Bindable<bool> mouseDisabled; private Bindable<bool> mouseDisabled;

View File

@ -97,7 +97,7 @@ namespace osu.Game.Screens.Menu
private OsuGame game { get; set; } private OsuGame game { get; set; }
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private NotificationOverlay notifications { get; set; } private NotificationOverlay notifications { get; set; }

View File

@ -1,4 +1,4 @@
// 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; using System;
@ -44,7 +44,7 @@ namespace osu.Game.Screens.Menu
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api) private void load(OsuColour colours, IAPIProvider api)
{ {
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {

View File

@ -246,7 +246,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
} }
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
private GetRoomScoresRequest request; private GetRoomScoresRequest request;

View File

@ -54,7 +54,7 @@ namespace osu.Game.Screens.Multi
private OsuGameBase game { get; set; } private OsuGameBase game { get; set; }
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private OsuLogo logo { get; set; } private OsuLogo logo { get; set; }
@ -163,7 +163,7 @@ namespace osu.Game.Screens.Multi
this.Push(new PlayerLoader(player)); this.Push(new PlayerLoader(player));
} }
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(IAPIProvider api, APIState state)
{ {
if (state != APIState.Online) if (state != APIState.Online)
forcefullyExit(); forcefullyExit();

View File

@ -32,7 +32,7 @@ namespace osu.Game.Screens.Multi.Play
private readonly PlaylistItem playlistItem; private readonly PlaylistItem playlistItem;
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
[Resolved] [Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } private IBindable<RulesetInfo> ruleset { get; set; }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Screens.Multi
private Bindable<FilterCriteria> currentFilter { get; set; } private Bindable<FilterCriteria> currentFilter { get; set; }
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
[Resolved] [Resolved]
private RulesetStore rulesets { get; set; } private RulesetStore rulesets { get; set; }

View File

@ -60,7 +60,7 @@ namespace osu.Game.Screens.Play
private RulesetInfo ruleset; private RulesetInfo ruleset;
private APIAccess api; private IAPIProvider api;
private SampleChannel sampleRestart; private SampleChannel sampleRestart;
@ -85,7 +85,7 @@ namespace osu.Game.Screens.Play
private GameplayClockContainer gameplayClockContainer; private GameplayClockContainer gameplayClockContainer;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, APIAccess api, OsuConfigManager config) private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config)
{ {
this.api = api; this.api = api;

View File

@ -36,7 +36,7 @@ namespace osu.Game.Screens.Select
private readonly FailRetryGraph failRetryGraph; private readonly FailRetryGraph failRetryGraph;
private readonly DimmedLoadingAnimation loading; private readonly DimmedLoadingAnimation loading;
private APIAccess api; private IAPIProvider api;
private ScheduledDelegate pendingBeatmapSwitch; private ScheduledDelegate pendingBeatmapSwitch;
@ -160,7 +160,7 @@ namespace osu.Game.Screens.Select
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(IAPIProvider api)
{ {
this.api = api; this.api = api;
} }

View File

@ -43,7 +43,7 @@ namespace osu.Game.Screens.Select.Leaderboards
private IBindable<RulesetInfo> ruleset { get; set; } private IBindable<RulesetInfo> ruleset { get; set; }
[Resolved] [Resolved]
private APIAccess api { get; set; } private IAPIProvider api { get; set; }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()