diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index 84cadeb2a1..0a16335c77 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -9,7 +9,6 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Screens.Play;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -43,7 +42,7 @@ namespace osu.Game.Rulesets.UI
///
/// The input manager for this RulesetContainer.
///
- internal readonly PlayerInputManager InputManager = new PlayerInputManager();
+ internal IHasReplayHandler ReplayInputManager => (IHasReplayHandler)KeyBindingInputManager;
///
/// The key conversion input manager for this RulesetContainer.
@@ -58,7 +57,7 @@ namespace osu.Game.Rulesets.UI
///
/// Whether we have a replay loaded currently.
///
- public bool HasReplayLoaded => InputManager.ReplayInputHandler != null;
+ public bool HasReplayLoaded => ReplayInputManager.ReplayInputHandler != null;
public abstract IEnumerable Objects { get; }
@@ -76,11 +75,7 @@ namespace osu.Game.Rulesets.UI
internal RulesetContainer(Ruleset ruleset)
{
Ruleset = ruleset;
- }
- [BackgroundDependencyLoader]
- private void load()
- {
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
}
@@ -113,7 +108,7 @@ namespace osu.Game.Rulesets.UI
public virtual void SetReplay(Replay replay)
{
Replay = replay;
- InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
+ ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
}
}
@@ -267,13 +262,12 @@ namespace osu.Game.Rulesets.UI
[BackgroundDependencyLoader]
private void load()
{
- InputManager.Add(content = new Container
+ KeyBindingInputManager.Add(content = new Container
{
RelativeSizeAxes = Axes.Both,
- Children = new[] { KeyBindingInputManager }
});
- AddInternal(InputManager);
+ AddInternal(KeyBindingInputManager);
KeyBindingInputManager.Add(Playfield = CreatePlayfield());
loadObjects();
@@ -283,8 +277,8 @@ namespace osu.Game.Rulesets.UI
{
base.SetReplay(replay);
- if (InputManager?.ReplayInputHandler != null)
- InputManager.ReplayInputHandler.ToScreenSpace = Playfield.ScaledContent.ToScreenSpace;
+ if (ReplayInputManager?.ReplayInputHandler != null)
+ ReplayInputManager.ReplayInputHandler.ToScreenSpace = input => Playfield.ScaledContent.ToScreenSpace(input);
}
///
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 6d22b2e91e..bd7e51e937 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -1,20 +1,176 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Input;
using osu.Framework.Input.Bindings;
+using osu.Framework.Timing;
+using osu.Game.Configuration;
using osu.Game.Input.Bindings;
+using osu.Game.Input.Handlers;
using osu.Game.Screens.Play;
+using OpenTK.Input;
namespace osu.Game.Rulesets.UI
{
- public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter
+ public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter, IHasReplayHandler
where T : struct
{
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
{
}
+ private List lastPressedActions = new List();
+
+ protected override void HandleNewState(InputState state)
+ {
+ base.HandleNewState(state);
+
+ var replayState = state as ReplayInputHandler.ReplayState;
+
+ if (replayState == null) return;
+
+ // Here we handle states specifically coming from a replay source.
+ // These have extra action information rather than keyboard keys or mouse buttons.
+
+ List newActions = replayState.PressedActions;
+
+ foreach (var released in lastPressedActions.Except(newActions))
+ PropagateReleased(KeyBindingInputQueue, released);
+
+ foreach (var pressed in newActions.Except(lastPressedActions))
+ PropagatePressed(KeyBindingInputQueue, pressed);
+
+ lastPressedActions = newActions;
+ }
+
+ private ManualClock clock;
+ private IFrameBasedClock parentClock;
+
+ private ReplayInputHandler replayInputHandler;
+ public ReplayInputHandler ReplayInputHandler
+ {
+ get
+ {
+ return replayInputHandler;
+ }
+ set
+ {
+ if (replayInputHandler != null) RemoveHandler(replayInputHandler);
+
+ replayInputHandler = value;
+ UseParentState = replayInputHandler == null;
+
+ if (replayInputHandler != null)
+ AddHandler(replayInputHandler);
+ }
+ }
+
+ private Bindable mouseDisabled;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuConfigManager config)
+ {
+ mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ //our clock will now be our parent's clock, but we want to replace this to allow manual control.
+ parentClock = Clock;
+
+ Clock = new FramedClock(clock = new ManualClock
+ {
+ CurrentTime = parentClock.CurrentTime,
+ Rate = parentClock.Rate,
+ });
+ }
+
+ ///
+ /// Whether we running up-to-date with our parent clock.
+ /// If not, we will need to keep processing children until we catch up.
+ ///
+ private bool requireMoreUpdateLoops;
+
+ ///
+ /// Whether we 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.
+ ///
+ private bool validState;
+
+ protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
+
+ private bool isAttached => replayInputHandler != null && !UseParentState;
+
+ private const int max_catch_up_updates_per_frame = 50;
+
+ public override bool UpdateSubTree()
+ {
+ requireMoreUpdateLoops = true;
+ validState = true;
+
+ int loops = 0;
+
+ while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
+ if (!base.UpdateSubTree())
+ return false;
+
+ return true;
+ }
+
+ protected override void Update()
+ {
+ if (parentClock == null) return;
+
+ clock.Rate = parentClock.Rate;
+ clock.IsRunning = parentClock.IsRunning;
+
+ if (!isAttached)
+ {
+ clock.CurrentTime = parentClock.CurrentTime;
+ }
+ else
+ {
+ double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
+
+ if (newTime == null)
+ {
+ // we shouldn't execute for this time value. probably waiting on more replay data.
+ validState = false;
+ return;
+ }
+
+ clock.CurrentTime = newTime.Value;
+ }
+
+ requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
+ base.Update();
+ }
+
+ protected override void TransformState(InputState state)
+ {
+ base.TransformState(state);
+
+ // we don't want to transform the state if a replay is present (for now, at least).
+ if (replayInputHandler != null) return;
+
+ var mouse = state.Mouse as Framework.Input.MouseState;
+
+ if (mouse != null)
+ {
+ if (mouseDisabled.Value)
+ {
+ mouse.SetPressed(MouseButton.Left, false);
+ mouse.SetPressed(MouseButton.Right, false);
+ }
+ }
+ }
+
public void Attach(KeyCounterCollection keyCounter)
{
var receptor = new ActionReceptor(keyCounter);
@@ -37,6 +193,11 @@ namespace osu.Game.Rulesets.UI
}
}
+ public interface IHasReplayHandler
+ {
+ ReplayInputHandler ReplayInputHandler { get; set; }
+ }
+
public interface ICanAttachKeyCounter
{
void Attach(KeyCounterCollection keyCounter);
diff --git a/osu.Game/Screens/Play/PlayerInputManager.cs b/osu.Game/Screens/Play/PlayerInputManager.cs
deleted file mode 100644
index f5e57f9e9d..0000000000
--- a/osu.Game/Screens/Play/PlayerInputManager.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using OpenTK.Input;
-using osu.Framework.Allocation;
-using osu.Framework.Configuration;
-using osu.Framework.Input;
-using osu.Framework.Timing;
-using osu.Game.Configuration;
-using osu.Game.Input.Handlers;
-
-namespace osu.Game.Screens.Play
-{
- public class PlayerInputManager : PassThroughInputManager
- {
- private ManualClock clock;
- private IFrameBasedClock parentClock;
-
- private ReplayInputHandler replayInputHandler;
- public ReplayInputHandler ReplayInputHandler
- {
- get
- {
- return replayInputHandler;
- }
- set
- {
- if (replayInputHandler != null) RemoveHandler(replayInputHandler);
-
- replayInputHandler = value;
- UseParentState = replayInputHandler == null;
-
- if (replayInputHandler != null)
- AddHandler(replayInputHandler);
- }
- }
-
- private Bindable mouseDisabled;
-
- [BackgroundDependencyLoader]
- private void load(OsuConfigManager config)
- {
- mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons);
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- //our clock will now be our parent's clock, but we want to replace this to allow manual control.
- parentClock = Clock;
-
- Clock = new FramedClock(clock = new ManualClock
- {
- CurrentTime = parentClock.CurrentTime,
- Rate = parentClock.Rate,
- });
- }
-
- ///
- /// Whether we running up-to-date with our parent clock.
- /// If not, we will need to keep processing children until we catch up.
- ///
- private bool requireMoreUpdateLoops;
-
- ///
- /// Whether we 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.
- ///
- private bool validState;
-
- protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
-
- private bool isAttached => replayInputHandler != null && !UseParentState;
-
- private const int max_catch_up_updates_per_frame = 50;
-
- public override bool UpdateSubTree()
- {
- requireMoreUpdateLoops = true;
- validState = true;
-
- int loops = 0;
-
- while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
- if (!base.UpdateSubTree())
- return false;
-
- return true;
- }
-
- protected override void Update()
- {
- if (parentClock == null) return;
-
- clock.Rate = parentClock.Rate;
- clock.IsRunning = parentClock.IsRunning;
-
- if (!isAttached)
- {
- clock.CurrentTime = parentClock.CurrentTime;
- }
- else
- {
- double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
-
- if (newTime == null)
- {
- // we shouldn't execute for this time value. probably waiting on more replay data.
- validState = false;
- return;
- }
-
- clock.CurrentTime = newTime.Value;
- }
-
- requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
- base.Update();
- }
-
- protected override void TransformState(InputState state)
- {
- base.TransformState(state);
-
- // we don't want to transform the state if a replay is present (for now, at least).
- if (replayInputHandler != null) return;
-
- var mouse = state.Mouse as Framework.Input.MouseState;
-
- if (mouse != null)
- {
- if (mouseDisabled.Value)
- {
- mouse.SetPressed(MouseButton.Left, false);
- mouse.SetPressed(MouseButton.Right, false);
- }
- }
- }
- }
-}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 32a8df6357..4e9e769cb7 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -321,7 +321,6 @@
-