mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 16:52:54 +08:00
Merge pull request #20157 from peppy/true-gameplay-rate
Refactor `TrueGameplayRate` to account for only gameplay adjustments, no matter what
This commit is contained in:
commit
9aab502adc
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
currentRotation += angle;
|
||||
// rate has to be applied each frame, because it's not guaranteed to be constant throughout playback
|
||||
// (see: ModTimeRamp)
|
||||
drawableSpinner.Result.RateAdjustedRotation += (float)(Math.Abs(angle) * (gameplayClock?.TrueGameplayRate ?? Clock.Rate));
|
||||
drawableSpinner.Result.RateAdjustedRotation += (float)(Math.Abs(angle) * (gameplayClock?.GetTrueGameplayRate() ?? Clock.Rate));
|
||||
}
|
||||
|
||||
private void resetState(DrawableHitObject obj)
|
||||
|
@ -1,8 +1,9 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
@ -13,21 +14,20 @@ namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestCase(0)]
|
||||
[TestCase(1)]
|
||||
public void TestTrueGameplayRateWithZeroAdjustment(double underlyingClockRate)
|
||||
public void TestTrueGameplayRateWithGameplayAdjustment(double underlyingClockRate)
|
||||
{
|
||||
var framedClock = new FramedClock(new ManualClock { Rate = underlyingClockRate });
|
||||
var gameplayClock = new TestGameplayClockContainer(framedClock);
|
||||
|
||||
Assert.That(gameplayClock.TrueGameplayRate, Is.EqualTo(0));
|
||||
Assert.That(gameplayClock.GetTrueGameplayRate(), Is.EqualTo(2));
|
||||
}
|
||||
|
||||
private class TestGameplayClockContainer : GameplayClockContainer
|
||||
{
|
||||
public override IEnumerable<double> NonGameplayAdjustments => new[] { 0.0 };
|
||||
|
||||
public TestGameplayClockContainer(IFrameBasedClock underlyingClock)
|
||||
: base(underlyingClock)
|
||||
{
|
||||
AdjustmentsFromMods.AddAdjustment(AdjustableProperty.Frequency, new BindableDouble(2.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void confirmNoTrackAdjustments()
|
||||
{
|
||||
AddAssert("track has no adjustments", () => Beatmap.Value.Track.AggregateFrequency.Value == 1);
|
||||
AddUntilStep("track has no adjustments", () => Beatmap.Value.Track.AggregateFrequency.Value, () => Is.EqualTo(1));
|
||||
}
|
||||
|
||||
private void restart() => AddStep("restart", () => Player.Restart());
|
||||
|
@ -13,9 +13,11 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -332,6 +334,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("player 2 playing from correct point in time", () => getPlayer(PLAYER_2_ID).ChildrenOfType<DrawableRuleset>().Single().FrameStableClock.CurrentTime > 30000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGameplayRateAdjust()
|
||||
{
|
||||
start(getPlayerIds(4), mods: new[] { new APIMod(new OsuModDoubleTime()) });
|
||||
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(getPlayerIds(4), 300);
|
||||
|
||||
AddUntilStep("wait for correct track speed", () => Beatmap.Value.Track.Rate, () => Is.EqualTo(1.5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayersLeaveWhileSpectating()
|
||||
{
|
||||
@ -420,7 +434,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void start(int userId, int? beatmapId = null) => start(new[] { userId }, beatmapId);
|
||||
|
||||
private void start(int[] userIds, int? beatmapId = null)
|
||||
private void start(int[] userIds, int? beatmapId = null, APIMod[]? mods = null)
|
||||
{
|
||||
AddStep("start play", () =>
|
||||
{
|
||||
@ -429,10 +443,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
var user = new MultiplayerRoomUser(id)
|
||||
{
|
||||
User = new APIUser { Id = id },
|
||||
Mods = mods ?? Array.Empty<APIMod>(),
|
||||
};
|
||||
|
||||
OnlinePlayDependencies.MultiplayerClient.AddUser(user.User, true);
|
||||
SpectatorClient.SendStartPlay(id, beatmapId ?? importedBeatmapId);
|
||||
OnlinePlayDependencies.MultiplayerClient.AddUser(user, true);
|
||||
SpectatorClient.SendStartPlay(id, beatmapId ?? importedBeatmapId, mods);
|
||||
|
||||
playingUsers.Add(user);
|
||||
}
|
||||
|
@ -2,15 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
@ -263,27 +261,11 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public FrameTimeInfo TimeInfo => framedClock.TimeInfo;
|
||||
|
||||
public double TrueGameplayRate
|
||||
{
|
||||
get
|
||||
{
|
||||
double baseRate = Rate;
|
||||
|
||||
foreach (double adjustment in NonGameplayAdjustments)
|
||||
{
|
||||
if (Precision.AlmostEquals(adjustment, 0))
|
||||
return 0;
|
||||
|
||||
baseRate /= adjustment;
|
||||
}
|
||||
|
||||
return baseRate;
|
||||
}
|
||||
}
|
||||
|
||||
public double StartTime => parentGameplayClock?.StartTime ?? 0;
|
||||
|
||||
public IEnumerable<double> NonGameplayAdjustments => parentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<double>();
|
||||
private readonly AudioAdjustments gameplayAdjustments = new AudioAdjustments();
|
||||
|
||||
public IAdjustableAudioComponent AdjustmentsFromMods => parentGameplayClock?.AdjustmentsFromMods ?? gameplayAdjustments;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -13,6 +14,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// </summary>
|
||||
public class MultiSpectatorPlayer : SpectatorPlayer
|
||||
{
|
||||
/// <summary>
|
||||
/// All adjustments applied to the clock of this <see cref="MultiSpectatorPlayer"/> which come from mods.
|
||||
/// </summary>
|
||||
public IAggregateAudioAdjustment ClockAdjustmentsFromMods => clockAdjustmentsFromMods;
|
||||
|
||||
private readonly AudioAdjustments clockAdjustmentsFromMods = new AudioAdjustments();
|
||||
private readonly SpectatorPlayerClock spectatorPlayerClock;
|
||||
|
||||
/// <summary>
|
||||
@ -53,6 +60,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
}
|
||||
|
||||
protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart)
|
||||
=> new GameplayClockContainer(spectatorPlayerClock);
|
||||
{
|
||||
var gameplayClockContainer = new GameplayClockContainer(spectatorPlayerClock);
|
||||
clockAdjustmentsFromMods.BindAdjustments(gameplayClockContainer.AdjustmentsFromMods);
|
||||
return gameplayClockContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -43,6 +44,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
[Resolved]
|
||||
private MultiplayerClient multiplayerClient { get; set; } = null!;
|
||||
|
||||
private IAggregateAudioAdjustment? boundAdjustments;
|
||||
|
||||
private readonly PlayerArea[] instances;
|
||||
private MasterGameplayClockContainer masterClockContainer = null!;
|
||||
private SpectatorSyncManager syncManager = null!;
|
||||
@ -157,6 +160,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
base.LoadComplete();
|
||||
|
||||
masterClockContainer.Reset();
|
||||
|
||||
// Start with adjustments from the first player to keep a sane state.
|
||||
bindAudioAdjustments(instances.First());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -169,11 +175,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
.OrderBy(i => Math.Abs(i.SpectatorPlayerClock.CurrentTime - syncManager.CurrentMasterTime))
|
||||
.FirstOrDefault();
|
||||
|
||||
// Only bind adjustments if there's actually a valid source, else just use the previous ones to ensure no sudden changes to audio.
|
||||
if (currentAudioSource != null)
|
||||
bindAudioAdjustments(currentAudioSource);
|
||||
|
||||
foreach (var instance in instances)
|
||||
instance.Mute = instance != currentAudioSource;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindAudioAdjustments(PlayerArea first)
|
||||
{
|
||||
if (boundAdjustments != null)
|
||||
masterClockContainer.AdjustmentsFromMods.UnbindAdjustments(boundAdjustments);
|
||||
|
||||
boundAdjustments = first.ClockAdjustmentsFromMods;
|
||||
masterClockContainer.AdjustmentsFromMods.BindAdjustments(boundAdjustments);
|
||||
}
|
||||
|
||||
private bool isCandidateAudioSource(SpectatorPlayerClock? clock)
|
||||
=> clock?.IsRunning == true && !clock.IsCatchingUp && !clock.WaitingOnFrames;
|
||||
|
||||
|
@ -42,6 +42,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// </summary>
|
||||
public readonly SpectatorPlayerClock SpectatorPlayerClock;
|
||||
|
||||
/// <summary>
|
||||
/// The clock adjustments applied by the <see cref="Player"/> loaded in this area.
|
||||
/// </summary>
|
||||
public IAggregateAudioAdjustment ClockAdjustmentsFromMods => clockAdjustmentsFromMods;
|
||||
|
||||
/// <summary>
|
||||
/// The currently-loaded score.
|
||||
/// </summary>
|
||||
@ -50,6 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||
|
||||
private readonly AudioAdjustments clockAdjustmentsFromMods = new AudioAdjustments();
|
||||
private readonly BindableDouble volumeAdjustment = new BindableDouble();
|
||||
private readonly Container gameplayContent;
|
||||
private readonly LoadingLayer loadingLayer;
|
||||
@ -97,6 +103,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
{
|
||||
var player = new MultiSpectatorPlayer(Score, SpectatorPlayerClock);
|
||||
player.OnGameplayStarted += () => OnGameplayStarted?.Invoke();
|
||||
|
||||
clockAdjustmentsFromMods.BindAdjustments(player.ClockAdjustmentsFromMods);
|
||||
|
||||
return player;
|
||||
}));
|
||||
|
||||
|
@ -2,15 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
@ -46,7 +44,7 @@ namespace osu.Game.Screens.Play
|
||||
/// </remarks>
|
||||
public double StartTime { get; protected set; }
|
||||
|
||||
public virtual IEnumerable<double> NonGameplayAdjustments => Enumerable.Empty<double>();
|
||||
public IAdjustableAudioComponent AdjustmentsFromMods { get; } = new AudioAdjustments();
|
||||
|
||||
private readonly BindableBool isPaused = new BindableBool(true);
|
||||
|
||||
@ -196,7 +194,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
void IAdjustableClock.Reset() => Reset();
|
||||
|
||||
public void ResetSpeedAdjustments() => throw new NotImplementedException();
|
||||
public virtual void ResetSpeedAdjustments()
|
||||
{
|
||||
}
|
||||
|
||||
double IAdjustableClock.Rate
|
||||
{
|
||||
@ -222,23 +222,5 @@ namespace osu.Game.Screens.Play
|
||||
public double FramesPerSecond => GameplayClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => GameplayClock.TimeInfo;
|
||||
|
||||
public double TrueGameplayRate
|
||||
{
|
||||
get
|
||||
{
|
||||
double baseRate = Rate;
|
||||
|
||||
foreach (double adjustment in NonGameplayAdjustments)
|
||||
{
|
||||
if (Precision.AlmostEquals(adjustment, 0))
|
||||
return 0;
|
||||
|
||||
baseRate /= adjustment;
|
||||
}
|
||||
|
||||
return baseRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
osu.Game/Screens/Play/GameplayClockExtensions.cs
Normal file
24
osu.Game/Screens/Play/GameplayClockExtensions.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public static class GameplayClockExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// The rate of gameplay when playback is at 100%.
|
||||
/// This excludes any seeking / user adjustments.
|
||||
/// </summary>
|
||||
public static double GetTrueGameplayRate(this IGameplayClock clock)
|
||||
{
|
||||
// To handle rewind, we still want to maintain the same direction as the underlying clock.
|
||||
double rate = clock.Rate == 0 ? 1 : Math.Sign(clock.Rate);
|
||||
|
||||
return rate
|
||||
* clock.AdjustmentsFromMods.AggregateFrequency.Value
|
||||
* clock.AdjustmentsFromMods.AggregateTempo.Value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// 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.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
@ -9,12 +9,6 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
public interface IGameplayClock : IFrameBasedClock
|
||||
{
|
||||
/// <summary>
|
||||
/// The rate of gameplay when playback is at 100%.
|
||||
/// This excludes any seeking / user adjustments.
|
||||
/// </summary>
|
||||
double TrueGameplayRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The time from which the clock should start. Will be seeked to on calling <see cref="GameplayClockContainer.Reset"/>.
|
||||
/// </summary>
|
||||
@ -25,9 +19,9 @@ namespace osu.Game.Screens.Play
|
||||
double StartTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All adjustments applied to this clock which don't come from gameplay or mods.
|
||||
/// All adjustments applied to this clock which come from mods.
|
||||
/// </summary>
|
||||
IEnumerable<double> NonGameplayAdjustments { get; }
|
||||
IAdjustableAudioComponent AdjustmentsFromMods { get; }
|
||||
|
||||
IBindable<bool> IsPaused { get; }
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
@ -11,6 +11,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
@ -41,9 +42,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
|
||||
private readonly double skipTargetTime;
|
||||
private readonly Track track;
|
||||
|
||||
private readonly List<Bindable<double>> nonGameplayAdjustments = new List<Bindable<double>>();
|
||||
private readonly double skipTargetTime;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the time at which the last <see cref="StopGameplayClock"/> call was triggered.
|
||||
@ -56,7 +57,8 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
private double? actualStopTime;
|
||||
|
||||
public override IEnumerable<double> NonGameplayAdjustments => nonGameplayAdjustments.Select(b => b.Value);
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new master gameplay clock container.
|
||||
@ -69,6 +71,8 @@ namespace osu.Game.Screens.Play
|
||||
this.beatmap = beatmap;
|
||||
this.skipTargetTime = skipTargetTime;
|
||||
|
||||
track = beatmap.Track;
|
||||
|
||||
StartTime = findEarliestStartTime();
|
||||
}
|
||||
|
||||
@ -195,15 +199,12 @@ namespace osu.Game.Screens.Play
|
||||
if (speedAdjustmentsApplied)
|
||||
return;
|
||||
|
||||
if (SourceClock is not Track track)
|
||||
return;
|
||||
musicController.ResetTrackAdjustments();
|
||||
|
||||
track.BindAdjustments(AdjustmentsFromMods);
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
|
||||
nonGameplayAdjustments.Add(GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
nonGameplayAdjustments.Add(UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = true;
|
||||
}
|
||||
|
||||
@ -212,15 +213,10 @@ namespace osu.Game.Screens.Play
|
||||
if (!speedAdjustmentsApplied)
|
||||
return;
|
||||
|
||||
if (SourceClock is not Track track)
|
||||
return;
|
||||
|
||||
track.UnbindAdjustments(AdjustmentsFromMods);
|
||||
track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
|
||||
nonGameplayAdjustments.Remove(GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
nonGameplayAdjustments.Remove(UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = false;
|
||||
}
|
||||
|
||||
|
@ -996,12 +996,8 @@ namespace osu.Game.Screens.Play
|
||||
foreach (var mod in GameplayState.Mods.OfType<IApplicableToHUD>())
|
||||
mod.ApplyToHUD(HUDOverlay);
|
||||
|
||||
// Our mods are local copies of the global mods so they need to be re-applied to the track.
|
||||
// This is done through the music controller (for now), because resetting speed adjustments on the beatmap track also removes adjustments provided by DrawableTrack.
|
||||
// Todo: In the future, player will receive in a track and will probably not have to worry about this...
|
||||
musicController.ResetTrackAdjustments();
|
||||
foreach (var mod in GameplayState.Mods.OfType<IApplicableToTrack>())
|
||||
mod.ApplyToTrack(musicController.CurrentTrack);
|
||||
mod.ApplyToTrack(GameplayClockContainer.AdjustmentsFromMods);
|
||||
|
||||
updateGameplayState();
|
||||
|
||||
|
@ -81,13 +81,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void Disconnect() => isConnected.Value = false;
|
||||
|
||||
public MultiplayerRoomUser AddUser(APIUser user, bool markAsPlaying = false)
|
||||
{
|
||||
var roomUser = new MultiplayerRoomUser(user.Id) { User = user };
|
||||
=> AddUser(new MultiplayerRoomUser(user.Id) { User = user }, markAsPlaying);
|
||||
|
||||
public MultiplayerRoomUser AddUser(MultiplayerRoomUser roomUser, bool markAsPlaying = false)
|
||||
{
|
||||
addUser(roomUser);
|
||||
|
||||
if (markAsPlaying)
|
||||
PlayingUserIds.Add(user.Id);
|
||||
PlayingUserIds.Add(roomUser.UserID);
|
||||
|
||||
return roomUser;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -37,6 +38,7 @@ namespace osu.Game.Tests.Visual.Spectator
|
||||
private readonly Dictionary<int, ReplayFrame> lastReceivedUserFrames = new Dictionary<int, ReplayFrame>();
|
||||
|
||||
private readonly Dictionary<int, int> userBeatmapDictionary = new Dictionary<int, int>();
|
||||
private readonly Dictionary<int, APIMod[]> userModsDictionary = new Dictionary<int, APIMod[]>();
|
||||
private readonly Dictionary<int, int> userNextFrameDictionary = new Dictionary<int, int>();
|
||||
|
||||
[Resolved]
|
||||
@ -52,9 +54,11 @@ namespace osu.Game.Tests.Visual.Spectator
|
||||
/// </summary>
|
||||
/// <param name="userId">The user to start play for.</param>
|
||||
/// <param name="beatmapId">The playing beatmap id.</param>
|
||||
public void SendStartPlay(int userId, int beatmapId)
|
||||
/// <param name="mods">The mods the user has applied.</param>
|
||||
public void SendStartPlay(int userId, int beatmapId, APIMod[]? mods = null)
|
||||
{
|
||||
userBeatmapDictionary[userId] = beatmapId;
|
||||
userModsDictionary[userId] = mods ?? Array.Empty<APIMod>();
|
||||
userNextFrameDictionary[userId] = 0;
|
||||
sendPlayingState(userId);
|
||||
}
|
||||
@ -73,10 +77,12 @@ namespace osu.Game.Tests.Visual.Spectator
|
||||
{
|
||||
BeatmapID = userBeatmapDictionary[userId],
|
||||
RulesetID = 0,
|
||||
Mods = userModsDictionary[userId],
|
||||
State = state
|
||||
});
|
||||
|
||||
userBeatmapDictionary.Remove(userId);
|
||||
userModsDictionary.Remove(userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -125,6 +131,7 @@ namespace osu.Game.Tests.Visual.Spectator
|
||||
// Track the local user's playing beatmap ID.
|
||||
Debug.Assert(state.BeatmapID != null);
|
||||
userBeatmapDictionary[api.LocalUser.Value.Id] = state.BeatmapID.Value;
|
||||
userModsDictionary[api.LocalUser.Value.Id] = state.Mods.ToArray();
|
||||
|
||||
return ((ISpectatorClient)this).UserBeganPlaying(api.LocalUser.Value.Id, state);
|
||||
}
|
||||
@ -158,6 +165,7 @@ namespace osu.Game.Tests.Visual.Spectator
|
||||
{
|
||||
BeatmapID = userBeatmapDictionary[userId],
|
||||
RulesetID = 0,
|
||||
Mods = userModsDictionary[userId],
|
||||
State = SpectatedUserState.Playing
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user