diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 62ae507419..036ec4d0f3 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -339,7 +339,7 @@ namespace osu.Game.Online.Chat
}
///
- /// Joins a channel if it has not already been joined.
+ /// Joins a channel if it has not already been joined. Must be called from the update thread.
///
/// The channel to join.
/// The joined channel. Note that this may not match the parameter channel as it is a backed object.
@@ -399,7 +399,11 @@ namespace osu.Game.Online.Chat
return channel;
}
- public void LeaveChannel(Channel channel)
+ ///
+ /// Leave the specified channel. Can be called from any thread.
+ ///
+ /// The channel to leave.
+ public void LeaveChannel(Channel channel) => Schedule(() =>
{
if (channel == null) return;
@@ -413,7 +417,7 @@ namespace osu.Game.Online.Chat
api.Queue(new LeaveChannelRequest(channel));
channel.Joined.Value = false;
}
- }
+ });
private long lastMessageId;
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 35852f60ea..e927951d0a 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -332,7 +332,7 @@ namespace osu.Game.Rulesets.Edit
EditorBeatmap.Add(hitObject);
if (EditorClock.CurrentTime < hitObject.StartTime)
- EditorClock.SeekTo(hitObject.StartTime);
+ EditorClock.SeekSmoothlyTo(hitObject.StartTime);
}
}
diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
index 9e9ac93d23..5a2214509c 100644
--- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
+++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs
@@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
return;
float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
- editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength);
+ editorClock.SeekSmoothlyTo(markerPos / DrawWidth * editorClock.TrackLength);
});
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index 0b45bd5597..5371beac60 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -170,7 +170,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (clickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != clickedBlueprint)
return false;
- EditorClock?.SeekTo(clickedBlueprint.HitObject.StartTime);
+ EditorClock?.SeekSmoothlyTo(clickedBlueprint.HitObject.StartTime);
return true;
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
index 12f7625bf9..666026e05e 100644
--- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs
@@ -155,12 +155,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
seekTrackToCurrent();
else if (!editorClock.IsRunning)
{
- // The track isn't running. There are two cases we have to be wary of:
- // 1) The user flick-drags on this timeline: We want the track to follow us
- // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time
+ // The track isn't running. There are three cases we have to be wary of:
+ // 1) The user flick-drags on this timeline and we are applying an interpolated seek on the clock, until interrupted by 2 or 3.
+ // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline; clicking a hitobject etc.). We want the timeline to track the clock's time.
+ // 3) An ongoing seek transform is running from an external seek. We want the timeline to track the clock's time.
- // The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally
- if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime)
+ // The simplest way to cover the first two cases is by checking whether the scroll position has changed and the audio hasn't been changed externally
+ // Checking IsSeeking covers the third case, where the transform may not have been applied yet.
+ if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime && !editorClock.IsSeeking)
seekTrackToCurrent();
else
scrollToTrackTime();
diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs
index 148eef6c93..ec0f5d7154 100644
--- a/osu.Game/Screens/Edit/EditorClock.cs
+++ b/osu.Game/Screens/Edit/EditorClock.cs
@@ -35,6 +35,11 @@ namespace osu.Game.Screens.Edit
private readonly Bindable seekingOrStopped = new Bindable(true);
+ ///
+ /// Whether a seek is currently in progress. True for the duration of a seek performed via .
+ ///
+ public bool IsSeeking { get; private set; }
+
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
{
@@ -111,7 +116,7 @@ namespace osu.Game.Screens.Edit
if (!snapped || ControlPointInfo.TimingPoints.Count == 0)
{
- SeekTo(seekTime);
+ SeekSmoothlyTo(seekTime);
return;
}
@@ -145,11 +150,11 @@ namespace osu.Game.Screens.Edit
// Ensure the sought point is within the boundaries
seekTime = Math.Clamp(seekTime, 0, TrackLength);
- SeekTo(seekTime);
+ SeekSmoothlyTo(seekTime);
}
///
- /// The current time of this clock, include any active transform seeks performed via .
+ /// The current time of this clock, include any active transform seeks performed via .
///
public double CurrentTimeAccurate =>
Transforms.OfType().FirstOrDefault()?.EndValue ?? CurrentTime;
@@ -176,12 +181,29 @@ namespace osu.Game.Screens.Edit
public bool Seek(double position)
{
- seekingOrStopped.Value = true;
+ seekingOrStopped.Value = IsSeeking = true;
ClearTransforms();
return underlyingClock.Seek(position);
}
+ ///
+ /// Seek smoothly to the provided destination.
+ /// Use to perform an immediate seek.
+ ///
+ ///
+ public void SeekSmoothlyTo(double seekDestination)
+ {
+ seekingOrStopped.Value = true;
+
+ if (IsRunning)
+ Seek(seekDestination);
+ else
+ {
+ transformSeekTo(seekDestination, transform_time, Easing.OutQuint);
+ }
+ }
+
public void ResetSpeedAdjustments() => underlyingClock.ResetSpeedAdjustments();
double IAdjustableClock.Rate
@@ -229,6 +251,8 @@ namespace osu.Game.Screens.Edit
{
if (seekingOrStopped.Value)
{
+ IsSeeking &= Transforms.Any();
+
if (track.Value?.IsRunning != true)
{
// seeking in the editor can happen while the track isn't running.
@@ -239,20 +263,10 @@ namespace osu.Game.Screens.Edit
// we are either running a seek tween or doing an immediate seek.
// in the case of an immediate seek the seeking bool will be set to false after one update.
// this allows for silencing hit sounds and the likes.
- seekingOrStopped.Value = Transforms.Any();
+ seekingOrStopped.Value = IsSeeking;
}
}
- public void SeekTo(double seekDestination)
- {
- seekingOrStopped.Value = true;
-
- if (IsRunning)
- Seek(seekDestination);
- else
- transformSeekTo(seekDestination, transform_time, Easing.OutQuint);
- }
-
private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None)
=> this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing));
diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs
index 89d3c36250..e4b9150df1 100644
--- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs
+++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs
@@ -206,7 +206,7 @@ namespace osu.Game.Screens.Edit.Timing
Action = () =>
{
selectedGroup.Value = controlGroup;
- clock.SeekTo(controlGroup.Time);
+ clock.SeekSmoothlyTo(controlGroup.Time);
};
}
diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs
index 8800215c2e..6da2866236 100644
--- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs
+++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchChatDisplay.cs
@@ -38,5 +38,11 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components
Channel.Value = channelManager?.JoinChannel(new Channel { Id = channelId.Value, Type = ChannelType.Multiplayer, Name = $"#lazermp_{roomId.Value}" });
}
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ channelManager?.LeaveChannel(Channel.Value);
+ }
}
}