mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 21:47:25 +08:00
Merge branch 'master' into disallow-multiplayer-restart-retry
This commit is contained in:
commit
76935b93b6
26
osu.Game/Extensions/TaskExtensions.cs
Normal file
26
osu.Game/Extensions/TaskExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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.Threading.Tasks;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
|
||||||
|
namespace osu.Game.Extensions
|
||||||
|
{
|
||||||
|
public static class TaskExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Denote a task which is to be run without local error handling logic, where failure is not catastrophic.
|
||||||
|
/// Avoids unobserved exceptions from being fired.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">The task.</param>
|
||||||
|
/// <param name="logOnError">Whether errors should be logged as important, or silently ignored.</param>
|
||||||
|
public static void CatchUnobservedExceptions(this Task task, bool logOnError = false)
|
||||||
|
{
|
||||||
|
task.ContinueWith(t =>
|
||||||
|
{
|
||||||
|
if (logOnError)
|
||||||
|
Logger.Log($"Error running task: {t.Exception?.Message ?? "unknown"}", LoggingTarget.Runtime, LogLevel.Important);
|
||||||
|
}, TaskContinuationOptions.NotOnRanToCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -122,7 +122,7 @@ namespace osu.Game.Online.RealtimeMultiplayer
|
|||||||
protected override Task<MultiplayerRoom> JoinRoom(long roomId)
|
protected override Task<MultiplayerRoom> JoinRoom(long roomId)
|
||||||
{
|
{
|
||||||
if (!isConnected.Value)
|
if (!isConnected.Value)
|
||||||
return Task.FromCanceled<MultiplayerRoom>(CancellationToken.None);
|
return Task.FromCanceled<MultiplayerRoom>(new CancellationToken(true));
|
||||||
|
|
||||||
return connection.InvokeAsync<MultiplayerRoom>(nameof(IMultiplayerServer.JoinRoom), roomId);
|
return connection.InvokeAsync<MultiplayerRoom>(nameof(IMultiplayerServer.JoinRoom), roomId);
|
||||||
}
|
}
|
||||||
@ -130,7 +130,11 @@ namespace osu.Game.Online.RealtimeMultiplayer
|
|||||||
public override async Task LeaveRoom()
|
public override async Task LeaveRoom()
|
||||||
{
|
{
|
||||||
if (!isConnected.Value)
|
if (!isConnected.Value)
|
||||||
|
{
|
||||||
|
// even if not connected, make sure the local room state can be cleaned up.
|
||||||
|
await base.LeaveRoom();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Room == null)
|
if (Room == null)
|
||||||
return;
|
return;
|
||||||
|
@ -11,8 +11,10 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
@ -75,6 +77,19 @@ namespace osu.Game.Online.RealtimeMultiplayer
|
|||||||
// Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise.
|
// Todo: This is temporary, until the multiplayer server returns the item id on match start or otherwise.
|
||||||
private int playlistItemId;
|
private int playlistItemId;
|
||||||
|
|
||||||
|
protected StatefulMultiplayerClient()
|
||||||
|
{
|
||||||
|
IsConnected.BindValueChanged(connected =>
|
||||||
|
{
|
||||||
|
// clean up local room state on server disconnect.
|
||||||
|
if (!connected.NewValue)
|
||||||
|
{
|
||||||
|
Logger.Log("Connection to multiplayer server was lost.", LoggingTarget.Runtime, LogLevel.Important);
|
||||||
|
LeaveRoom().CatchUnobservedExceptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Joins the <see cref="MultiplayerRoom"/> for a given API <see cref="Room"/>.
|
/// Joins the <see cref="MultiplayerRoom"/> for a given API <see cref="Room"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -354,7 +369,6 @@ namespace osu.Game.Online.RealtimeMultiplayer
|
|||||||
if (Room == null)
|
if (Room == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update a few properties of the room instantaneously.
|
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
if (Room == null)
|
if (Room == null)
|
||||||
@ -362,6 +376,7 @@ namespace osu.Game.Online.RealtimeMultiplayer
|
|||||||
|
|
||||||
Debug.Assert(apiRoom != null);
|
Debug.Assert(apiRoom != null);
|
||||||
|
|
||||||
|
// Update a few properties of the room instantaneously.
|
||||||
Room.Settings = settings;
|
Room.Settings = settings;
|
||||||
apiRoom.Name.Value = Room.Settings.Name;
|
apiRoom.Name.Value = Room.Settings.Name;
|
||||||
|
|
||||||
@ -370,12 +385,12 @@ namespace osu.Game.Online.RealtimeMultiplayer
|
|||||||
apiRoom.Playlist.Clear();
|
apiRoom.Playlist.Clear();
|
||||||
|
|
||||||
RoomChanged?.Invoke();
|
RoomChanged?.Invoke();
|
||||||
});
|
|
||||||
|
|
||||||
var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId);
|
var req = new GetBeatmapSetRequest(settings.BeatmapID, BeatmapSetLookupType.BeatmapId);
|
||||||
req.Success += res => updatePlaylist(settings, res);
|
req.Success += res => updatePlaylist(settings, res);
|
||||||
|
|
||||||
api.Queue(req);
|
api.Queue(req);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet)
|
private void updatePlaylist(MultiplayerRoomSettings settings, APIBeatmapSet onlineSet)
|
||||||
|
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination.
|
/// Given a minimal set of inputs, return the computed score and accuracy for the tracked beatmap / mods combination, at the current point in time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mode">The <see cref="ScoringMode"/> to compute the total score in.</param>
|
/// <param name="mode">The <see cref="ScoringMode"/> to compute the total score in.</param>
|
||||||
/// <param name="maxCombo">The maximum combo achievable in the beatmap.</param>
|
/// <param name="maxCombo">The maximum combo achievable in the beatmap.</param>
|
||||||
@ -252,15 +252,28 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value;
|
computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
double accuracy = calculateAccuracyRatio(computedBaseScore);
|
double pointInTimeAccuracy = calculateAccuracyRatio(computedBaseScore, true);
|
||||||
double comboRatio = calculateComboRatio(maxCombo);
|
double comboRatio = calculateComboRatio(maxCombo);
|
||||||
|
|
||||||
double score = GetScore(mode, maxAchievableCombo, accuracy, comboRatio, scoreResultCounts);
|
double score = GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), comboRatio, scoreResultCounts);
|
||||||
|
|
||||||
return (score, accuracy);
|
return (score, pointInTimeAccuracy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the accuracy fraction for the provided base score.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseScore">The score to be used for accuracy calculation.</param>
|
||||||
|
/// <param name="preferRolling">Whether the rolling base score should be used (ie. for the current point in time based on Apply/Reverted results).</param>
|
||||||
|
/// <returns>The computed accuracy.</returns>
|
||||||
|
private double calculateAccuracyRatio(double baseScore, bool preferRolling = false)
|
||||||
|
{
|
||||||
|
if (preferRolling && rollingMaxBaseScore != 0)
|
||||||
|
return baseScore / rollingMaxBaseScore;
|
||||||
|
|
||||||
|
return maxBaseScore > 0 ? baseScore / maxBaseScore : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calculateAccuracyRatio(double baseScore) => maxBaseScore > 0 ? baseScore / maxBaseScore : 0;
|
|
||||||
private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1;
|
private double calculateComboRatio(int maxCombo) => maxAchievableCombo > 0 ? (double)maxCombo / maxAchievableCombo : 1;
|
||||||
|
|
||||||
private double getBonusScore(Dictionary<HitResult, int> statistics)
|
private double getBonusScore(Dictionary<HitResult, int> statistics)
|
||||||
|
@ -184,7 +184,7 @@ namespace osu.Game.Screens.Multi.Lounge
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Push a room as a new subscreen.
|
/// Push a room as a new subscreen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Open(Room room)
|
public virtual void Open(Room room)
|
||||||
{
|
{
|
||||||
// Handles the case where a room is clicked 3 times in quick succession
|
// Handles the case where a room is clicked 3 times in quick succession
|
||||||
if (!this.IsCurrentScreen())
|
if (!this.IsCurrentScreen())
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Multi.Match
|
namespace osu.Game.Screens.Multi.Match
|
||||||
@ -23,6 +24,9 @@ namespace osu.Game.Screens.Multi.Match
|
|||||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapManager beatmapManager { get; set; }
|
private BeatmapManager beatmapManager { get; set; }
|
||||||
|
|
||||||
@ -39,6 +43,34 @@ namespace osu.Game.Screens.Multi.Match
|
|||||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnEntering(IScreen last)
|
||||||
|
{
|
||||||
|
base.OnEntering(last);
|
||||||
|
beginHandlingTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSuspending(IScreen next)
|
||||||
|
{
|
||||||
|
endHandlingTrack();
|
||||||
|
base.OnSuspending(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResuming(IScreen last)
|
||||||
|
{
|
||||||
|
base.OnResuming(last);
|
||||||
|
beginHandlingTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnExiting(IScreen next)
|
||||||
|
{
|
||||||
|
RoomManager?.PartRoom();
|
||||||
|
Mods.Value = Array.Empty<Mod>();
|
||||||
|
|
||||||
|
endHandlingTrack();
|
||||||
|
|
||||||
|
return base.OnExiting(next);
|
||||||
|
}
|
||||||
|
|
||||||
private void selectedItemChanged()
|
private void selectedItemChanged()
|
||||||
{
|
{
|
||||||
updateWorkingBeatmap();
|
updateWorkingBeatmap();
|
||||||
@ -63,12 +95,42 @@ namespace osu.Game.Screens.Multi.Match
|
|||||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
private void beginHandlingTrack()
|
||||||
{
|
{
|
||||||
RoomManager?.PartRoom();
|
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
||||||
Mods.Value = Array.Empty<Mod>();
|
}
|
||||||
|
|
||||||
return base.OnExiting(next);
|
private void endHandlingTrack()
|
||||||
|
{
|
||||||
|
Beatmap.ValueChanged -= applyLoopingToTrack;
|
||||||
|
cancelTrackLooping();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyLoopingToTrack(ValueChangedEvent<WorkingBeatmap> _ = null)
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var track = Beatmap.Value?.Track;
|
||||||
|
|
||||||
|
if (track != null)
|
||||||
|
{
|
||||||
|
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||||
|
track.Looping = true;
|
||||||
|
|
||||||
|
music?.EnsurePlayingSomething();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelTrackLooping()
|
||||||
|
{
|
||||||
|
var track = Beatmap?.Value?.Track;
|
||||||
|
|
||||||
|
if (track != null)
|
||||||
|
{
|
||||||
|
track.Looping = false;
|
||||||
|
track.RestartPoint = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Colour;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -22,7 +21,6 @@ using osu.Game.Screens.Multi.Components;
|
|||||||
using osu.Game.Screens.Multi.Lounge;
|
using osu.Game.Screens.Multi.Lounge;
|
||||||
using osu.Game.Screens.Multi.Lounge.Components;
|
using osu.Game.Screens.Multi.Lounge.Components;
|
||||||
using osu.Game.Screens.Multi.Match;
|
using osu.Game.Screens.Multi.Match;
|
||||||
using osu.Game.Screens.Multi.Match.Components;
|
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -130,12 +128,18 @@ namespace osu.Game.Screens.Multi
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Header(screenStack),
|
new Header(screenStack),
|
||||||
createButton = new CreateRoomButton
|
createButton = CreateNewMultiplayerGameButton().With(button =>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
button.Anchor = Anchor.TopRight;
|
||||||
Origin = Anchor.TopRight,
|
button.Origin = Anchor.TopRight;
|
||||||
Action = () => OpenNewRoom()
|
button.Size = new Vector2(150, Header.HEIGHT - 20);
|
||||||
},
|
button.Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = 10,
|
||||||
|
Right = 10 + HORIZONTAL_OVERFLOW_PADDING,
|
||||||
|
};
|
||||||
|
button.Action = () => OpenNewRoom();
|
||||||
|
}),
|
||||||
RoomManager = CreateRoomManager()
|
RoomManager = CreateRoomManager()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -198,8 +202,6 @@ namespace osu.Game.Screens.Multi
|
|||||||
{
|
{
|
||||||
this.FadeIn();
|
this.FadeIn();
|
||||||
waves.Show();
|
waves.Show();
|
||||||
|
|
||||||
beginHandlingTrack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnResuming(IScreen last)
|
public override void OnResuming(IScreen last)
|
||||||
@ -209,8 +211,6 @@ namespace osu.Game.Screens.Multi
|
|||||||
|
|
||||||
base.OnResuming(last);
|
base.OnResuming(last);
|
||||||
|
|
||||||
beginHandlingTrack();
|
|
||||||
|
|
||||||
UpdatePollingRate(isIdle.Value);
|
UpdatePollingRate(isIdle.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,8 +219,6 @@ namespace osu.Game.Screens.Multi
|
|||||||
this.ScaleTo(1.1f, 250, Easing.InSine);
|
this.ScaleTo(1.1f, 250, Easing.InSine);
|
||||||
this.FadeOut(250);
|
this.FadeOut(250);
|
||||||
|
|
||||||
endHandlingTrack();
|
|
||||||
|
|
||||||
UpdatePollingRate(isIdle.Value);
|
UpdatePollingRate(isIdle.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,8 +233,6 @@ namespace osu.Game.Screens.Multi
|
|||||||
if (screenStack.CurrentScreen != null)
|
if (screenStack.CurrentScreen != null)
|
||||||
loungeSubScreen.MakeCurrent();
|
loungeSubScreen.MakeCurrent();
|
||||||
|
|
||||||
endHandlingTrack();
|
|
||||||
|
|
||||||
base.OnExiting(next);
|
base.OnExiting(next);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -275,17 +271,6 @@ namespace osu.Game.Screens.Multi
|
|||||||
/// <returns>The created <see cref="Room"/>.</returns>
|
/// <returns>The created <see cref="Room"/>.</returns>
|
||||||
protected virtual Room CreateNewRoom() => new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } };
|
protected virtual Room CreateNewRoom() => new Room { Name = { Value = $"{api.LocalUser}'s awesome room" } };
|
||||||
|
|
||||||
private void beginHandlingTrack()
|
|
||||||
{
|
|
||||||
Beatmap.BindValueChanged(updateTrack, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void endHandlingTrack()
|
|
||||||
{
|
|
||||||
cancelLooping();
|
|
||||||
Beatmap.ValueChanged -= updateTrack;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void screenPushed(IScreen lastScreen, IScreen newScreen)
|
private void screenPushed(IScreen lastScreen, IScreen newScreen)
|
||||||
{
|
{
|
||||||
subScreenChanged(lastScreen, newScreen);
|
subScreenChanged(lastScreen, newScreen);
|
||||||
@ -322,47 +307,16 @@ namespace osu.Game.Screens.Multi
|
|||||||
|
|
||||||
UpdatePollingRate(isIdle.Value);
|
UpdatePollingRate(isIdle.Value);
|
||||||
createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200);
|
createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200);
|
||||||
|
|
||||||
updateTrack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IScreen CurrentSubScreen => screenStack.CurrentScreen;
|
protected IScreen CurrentSubScreen => screenStack.CurrentScreen;
|
||||||
|
|
||||||
private void updateTrack(ValueChangedEvent<WorkingBeatmap> _ = null)
|
|
||||||
{
|
|
||||||
if (screenStack.CurrentScreen is RoomSubScreen)
|
|
||||||
{
|
|
||||||
var track = Beatmap.Value?.Track;
|
|
||||||
|
|
||||||
if (track != null)
|
|
||||||
{
|
|
||||||
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
|
||||||
track.Looping = true;
|
|
||||||
|
|
||||||
music?.EnsurePlayingSomething();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cancelLooping();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelLooping()
|
|
||||||
{
|
|
||||||
var track = Beatmap?.Value?.Track;
|
|
||||||
|
|
||||||
if (track != null)
|
|
||||||
{
|
|
||||||
track.Looping = false;
|
|
||||||
track.RestartPoint = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract RoomManager CreateRoomManager();
|
protected abstract RoomManager CreateRoomManager();
|
||||||
|
|
||||||
protected abstract LoungeSubScreen CreateLounge();
|
protected abstract LoungeSubScreen CreateLounge();
|
||||||
|
|
||||||
|
protected abstract OsuButton CreateNewMultiplayerGameButton();
|
||||||
|
|
||||||
private class MultiplayerWaveContainer : WaveContainer
|
private class MultiplayerWaveContainer : WaveContainer
|
||||||
{
|
{
|
||||||
protected override bool StartHidden => true;
|
protected override bool StartHidden => true;
|
||||||
@ -385,26 +339,5 @@ namespace osu.Game.Screens.Multi
|
|||||||
protected override double TransformDuration => 200;
|
protected override double TransformDuration => 200;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateRoomButton : PurpleTriangleButton
|
|
||||||
{
|
|
||||||
public CreateRoomButton()
|
|
||||||
{
|
|
||||||
Size = new Vector2(150, Header.HEIGHT - 20);
|
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Top = 10,
|
|
||||||
Right = 10 + HORIZONTAL_OVERFLOW_PADDING,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
Triangles.TriangleScale = 1.5f;
|
|
||||||
|
|
||||||
Text = "Create room";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Online.RealtimeMultiplayer;
|
||||||
|
using osu.Game.Screens.Multi.Match.Components;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
||||||
|
{
|
||||||
|
public class CreateRealtimeMatchButton : PurpleTriangleButton
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(StatefulMultiplayerClient multiplayerClient)
|
||||||
|
{
|
||||||
|
Triangles.TriangleScale = 1.5f;
|
||||||
|
|
||||||
|
Text = "Create match";
|
||||||
|
|
||||||
|
((IBindable<bool>)Enabled).BindTo(multiplayerClient.IsConnected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Extensions.ExceptionExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -299,7 +300,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match
|
|||||||
if (t.IsCompletedSuccessfully)
|
if (t.IsCompletedSuccessfully)
|
||||||
onSuccess(currentRoom.Value);
|
onSuccess(currentRoom.Value);
|
||||||
else
|
else
|
||||||
onError(t.Exception?.Message ?? "Error changing settings.");
|
onError(t.Exception?.AsSingular().Message ?? "Error changing settings.");
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -7,6 +7,7 @@ using JetBrains.Annotations;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -105,13 +106,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Match
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (localUser.State == MultiplayerUserState.Idle)
|
if (localUser.State == MultiplayerUserState.Idle)
|
||||||
Client.ChangeState(MultiplayerUserState.Ready);
|
Client.ChangeState(MultiplayerUserState.Ready).CatchUnobservedExceptions(true);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Room?.Host?.Equals(localUser) == true)
|
if (Room?.Host?.Equals(localUser) == true)
|
||||||
Client.StartMatch();
|
Client.StartMatch().CatchUnobservedExceptions(true);
|
||||||
else
|
else
|
||||||
Client.ChangeState(MultiplayerUserState.Idle);
|
Client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -176,7 +177,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer.Participants
|
|||||||
if (Room.Host?.UserID != api.LocalUser.Value.Id)
|
if (Room.Host?.UserID != api.LocalUser.Value.Id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Client.TransferHost(targetUser);
|
Client.TransferHost(targetUser).CatchUnobservedExceptions(true);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
|
using osu.Game.Online.RealtimeMultiplayer;
|
||||||
using osu.Game.Screens.Multi.Lounge;
|
using osu.Game.Screens.Multi.Lounge;
|
||||||
using osu.Game.Screens.Multi.Lounge.Components;
|
using osu.Game.Screens.Multi.Lounge.Components;
|
||||||
using osu.Game.Screens.Multi.Match;
|
using osu.Game.Screens.Multi.Match;
|
||||||
@ -13,5 +16,19 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
protected override FilterControl CreateFilterControl() => new RealtimeFilterControl();
|
protected override FilterControl CreateFilterControl() => new RealtimeFilterControl();
|
||||||
|
|
||||||
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new RealtimeMatchSubScreen(room);
|
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new RealtimeMatchSubScreen(room);
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private StatefulMultiplayerClient client { get; set; }
|
||||||
|
|
||||||
|
public override void Open(Room room)
|
||||||
|
{
|
||||||
|
if (!client.IsConnected.Value)
|
||||||
|
{
|
||||||
|
Logger.Log("Not currently connected to the multiplayer server.", LoggingTarget.Runtime, LogLevel.Important);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Open(room);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,14 +58,17 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
|
|
||||||
client.ChangeSettings(item: item).ContinueWith(t =>
|
client.ChangeSettings(item: item).ContinueWith(t =>
|
||||||
{
|
{
|
||||||
return Schedule(() =>
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
loadingLayer.Hide();
|
loadingLayer.Hide();
|
||||||
|
|
||||||
if (t.IsCompletedSuccessfully)
|
if (t.IsCompletedSuccessfully)
|
||||||
this.Exit();
|
this.Exit();
|
||||||
else
|
else
|
||||||
|
{
|
||||||
Logger.Log($"Could not use current beatmap ({t.Exception?.Message})", level: LogLevel.Important);
|
Logger.Log($"Could not use current beatmap ({t.Exception?.Message})", level: LogLevel.Important);
|
||||||
|
Carousel.AllowSelection = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
// 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.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
@ -16,6 +18,7 @@ using osu.Game.Screens.Multi.RealtimeMultiplayer.Match;
|
|||||||
using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants;
|
using osu.Game.Screens.Multi.RealtimeMultiplayer.Participants;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using ParticipantsList = osu.Game.Screens.Multi.RealtimeMultiplayer.Participants.ParticipantsList;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
||||||
{
|
{
|
||||||
@ -34,6 +37,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
|
|
||||||
private RealtimeMatchSettingsOverlay settingsOverlay;
|
private RealtimeMatchSettingsOverlay settingsOverlay;
|
||||||
|
|
||||||
|
private IBindable<bool> isConnected;
|
||||||
|
|
||||||
public RealtimeMatchSubScreen(Room room)
|
public RealtimeMatchSubScreen(Room room)
|
||||||
{
|
{
|
||||||
Title = room.RoomID.Value == null ? "New match" : room.Name.Value;
|
Title = room.RoomID.Value == null ? "New match" : room.Name.Value;
|
||||||
@ -102,7 +107,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
new Drawable[] { new ParticipantsListHeader() },
|
new Drawable[] { new ParticipantsListHeader() },
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new Participants.ParticipantsList
|
new ParticipantsList
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
@ -173,6 +178,13 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||||
|
|
||||||
client.LoadRequested += onLoadRequested;
|
client.LoadRequested += onLoadRequested;
|
||||||
|
|
||||||
|
isConnected = client.IsConnected.GetBoundCopy();
|
||||||
|
isConnected.BindValueChanged(connected =>
|
||||||
|
{
|
||||||
|
if (!connected.NewValue)
|
||||||
|
Schedule(this.Exit);
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnBackButton()
|
public override bool OnBackButton()
|
||||||
@ -188,7 +200,14 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
|
|
||||||
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault();
|
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault();
|
||||||
|
|
||||||
private void onLoadRequested() => multiplayer?.Push(new PlayerLoader(() => new RealtimePlayer(SelectedItem.Value)));
|
private void onLoadRequested()
|
||||||
|
{
|
||||||
|
Debug.Assert(client.Room != null);
|
||||||
|
|
||||||
|
int[] userIds = client.Room.Users.Where(u => u.State >= MultiplayerUserState.WaitingForLoad).Select(u => u.UserID).ToArray();
|
||||||
|
|
||||||
|
multiplayer?.Push(new PlayerLoader(() => new RealtimePlayer(SelectedItem.Value, userIds)));
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.RealtimeMultiplayer;
|
using osu.Game.Online.RealtimeMultiplayer;
|
||||||
using osu.Game.Screens.Multi.Components;
|
using osu.Game.Screens.Multi.Components;
|
||||||
@ -21,7 +23,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
base.OnResuming(last);
|
base.OnResuming(last);
|
||||||
|
|
||||||
if (client.Room != null)
|
if (client.Room != null)
|
||||||
client.ChangeState(MultiplayerUserState.Idle);
|
client.ChangeState(MultiplayerUserState.Idle).CatchUnobservedExceptions(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdatePollingRate(bool isIdle)
|
protected override void UpdatePollingRate(bool isIdle)
|
||||||
@ -63,5 +65,7 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
protected override RoomManager CreateRoomManager() => new RealtimeRoomManager();
|
protected override RoomManager CreateRoomManager() => new RealtimeRoomManager();
|
||||||
|
|
||||||
protected override LoungeSubScreen CreateLounge() => new RealtimeLoungeSubScreen();
|
protected override LoungeSubScreen CreateLounge() => new RealtimeLoungeSubScreen();
|
||||||
|
|
||||||
|
protected override OsuButton CreateNewMultiplayerGameButton() => new CreateRealtimeMatchButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,18 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.RealtimeMultiplayer;
|
using osu.Game.Online.RealtimeMultiplayer;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Multi.Play;
|
using osu.Game.Screens.Multi.Play;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
||||||
{
|
{
|
||||||
@ -28,16 +31,29 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private StatefulMultiplayerClient client { get; set; }
|
private StatefulMultiplayerClient client { get; set; }
|
||||||
|
|
||||||
|
private IBindable<bool> isConnected;
|
||||||
|
|
||||||
private readonly TaskCompletionSource<bool> resultsReady = new TaskCompletionSource<bool>();
|
private readonly TaskCompletionSource<bool> resultsReady = new TaskCompletionSource<bool>();
|
||||||
private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim();
|
private readonly ManualResetEventSlim startedEvent = new ManualResetEventSlim();
|
||||||
|
|
||||||
public RealtimePlayer(PlaylistItem playlistItem)
|
[CanBeNull]
|
||||||
|
private MultiplayerGameplayLeaderboard leaderboard;
|
||||||
|
|
||||||
|
private readonly int[] userIds;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a multiplayer player.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="playlistItem">The playlist item to be played.</param>
|
||||||
|
/// <param name="userIds">The users which are participating in this game.</param>
|
||||||
|
public RealtimePlayer(PlaylistItem playlistItem, int[] userIds)
|
||||||
: base(playlistItem, new PlayerConfiguration
|
: base(playlistItem, new PlayerConfiguration
|
||||||
{
|
{
|
||||||
AllowPause = false,
|
AllowPause = false,
|
||||||
AllowRestart = false,
|
AllowRestart = false,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
this.userIds = userIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -48,18 +64,57 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
|
|
||||||
client.MatchStarted += onMatchStarted;
|
client.MatchStarted += onMatchStarted;
|
||||||
client.ResultsReady += onResultsReady;
|
client.ResultsReady += onResultsReady;
|
||||||
client.ChangeState(MultiplayerUserState.Loaded);
|
|
||||||
|
isConnected = client.IsConnected.GetBoundCopy();
|
||||||
|
isConnected.BindValueChanged(connected =>
|
||||||
|
{
|
||||||
|
if (!connected.NewValue)
|
||||||
|
{
|
||||||
|
// messaging to the user about this disconnect will be provided by the RealtimeMatchSubScreen.
|
||||||
|
failAndBail();
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
client.ChangeState(MultiplayerUserState.Loaded)
|
||||||
|
.ContinueWith(task => failAndBail(task.Exception?.Message ?? "Server error"), TaskContinuationOptions.NotOnRanToCompletion);
|
||||||
|
|
||||||
if (!startedEvent.Wait(TimeSpan.FromSeconds(30)))
|
if (!startedEvent.Wait(TimeSpan.FromSeconds(30)))
|
||||||
{
|
{
|
||||||
Logger.Log("Failed to start the multiplayer match in time.", LoggingTarget.Runtime, LogLevel.Important);
|
failAndBail("Failed to start the multiplayer match in time.");
|
||||||
|
return;
|
||||||
Schedule(() =>
|
|
||||||
{
|
|
||||||
ValidForResume = false;
|
|
||||||
this.Exit();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.Assert(client.Room != null);
|
||||||
|
|
||||||
|
// todo: this should be implemented via a custom HUD implementation, and correctly masked to the main content area.
|
||||||
|
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(ScoreProcessor, userIds), HUDOverlay.Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void failAndBail(string message = null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(message))
|
||||||
|
Logger.Log(message, LoggingTarget.Runtime, LogLevel.Important);
|
||||||
|
|
||||||
|
startedEvent.Set();
|
||||||
|
Schedule(() => PerformExit(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
adjustLeaderboardPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustLeaderboardPosition()
|
||||||
|
{
|
||||||
|
if (leaderboard == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const float padding = 44; // enough margin to avoid the hit error display.
|
||||||
|
|
||||||
|
leaderboard.Position = new Vector2(
|
||||||
|
padding,
|
||||||
|
padding + HUDOverlay.TopScoringElementsHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMatchStarted() => startedEvent.Set();
|
private void onMatchStarted() => startedEvent.Set();
|
||||||
|
@ -7,7 +7,9 @@ using System.Diagnostics;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ExceptionExtensions;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Online.Multiplayer.RoomStatuses;
|
using osu.Game.Online.Multiplayer.RoomStatuses;
|
||||||
using osu.Game.Online.RealtimeMultiplayer;
|
using osu.Game.Online.RealtimeMultiplayer;
|
||||||
@ -43,6 +45,12 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
|
|
||||||
public override void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
public override void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||||
{
|
{
|
||||||
|
if (!multiplayerClient.IsConnected.Value)
|
||||||
|
{
|
||||||
|
onError?.Invoke("Not currently connected to the multiplayer server.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join.
|
// this is done here as a pre-check to avoid clicking on already closed rooms in the lounge from triggering a server join.
|
||||||
// should probably be done at a higher level, but due to the current structure of things this is the easiest place for now.
|
// should probably be done at a higher level, but due to the current structure of things this is the easiest place for now.
|
||||||
if (room.Status.Value is RoomStatusEnded)
|
if (room.Status.Value is RoomStatusEnded)
|
||||||
@ -62,7 +70,8 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
var joinedRoom = JoinedRoom.Value;
|
var joinedRoom = JoinedRoom.Value;
|
||||||
|
|
||||||
base.PartRoom();
|
base.PartRoom();
|
||||||
multiplayerClient.LeaveRoom();
|
|
||||||
|
multiplayerClient.LeaveRoom().CatchUnobservedExceptions();
|
||||||
|
|
||||||
// Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case.
|
// Todo: This is not the way to do this. Basically when we're the only participant and the room closes, there's no way to know if this is actually the case.
|
||||||
// This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling.
|
// This is delayed one frame because upon exiting the match subscreen, multiplayer updates the polling rate and messes with polling.
|
||||||
@ -77,15 +86,21 @@ namespace osu.Game.Screens.Multi.RealtimeMultiplayer
|
|||||||
{
|
{
|
||||||
Debug.Assert(room.RoomID.Value != null);
|
Debug.Assert(room.RoomID.Value != null);
|
||||||
|
|
||||||
var joinTask = multiplayerClient.JoinRoom(room);
|
multiplayerClient.JoinRoom(room).ContinueWith(t =>
|
||||||
joinTask.ContinueWith(_ => Schedule(() => onSuccess?.Invoke(room)), TaskContinuationOptions.OnlyOnRanToCompletion);
|
|
||||||
joinTask.ContinueWith(t =>
|
|
||||||
{
|
{
|
||||||
PartRoom();
|
if (t.IsCompletedSuccessfully)
|
||||||
|
Schedule(() => onSuccess?.Invoke(room));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const string message = "Failed to join multiplayer room.";
|
||||||
|
|
||||||
if (t.Exception != null)
|
if (t.Exception != null)
|
||||||
Logger.Error(t.Exception, "Failed to join multiplayer room.");
|
Logger.Error(t.Exception, message);
|
||||||
Schedule(() => onError?.Invoke(t.Exception?.ToString() ?? string.Empty));
|
|
||||||
}, TaskContinuationOptions.NotOnRanToCompletion);
|
PartRoom();
|
||||||
|
Schedule(() => onError?.Invoke(t.Exception?.AsSingular().Message ?? message));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePolling()
|
private void updatePolling()
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Game.Screens.Multi.Match.Components;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Multi.Timeshift
|
||||||
|
{
|
||||||
|
public class CreateTimeshiftRoomButton : PurpleTriangleButton
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Triangles.TriangleScale = 1.5f;
|
||||||
|
|
||||||
|
Text = "Create room";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Screens.Multi.Components;
|
using osu.Game.Screens.Multi.Components;
|
||||||
using osu.Game.Screens.Multi.Lounge;
|
using osu.Game.Screens.Multi.Lounge;
|
||||||
using osu.Game.Screens.Multi.Match;
|
using osu.Game.Screens.Multi.Match;
|
||||||
@ -47,5 +48,7 @@ namespace osu.Game.Screens.Multi.Timeshift
|
|||||||
protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager();
|
protected override RoomManager CreateRoomManager() => new TimeshiftRoomManager();
|
||||||
|
|
||||||
protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen();
|
protected override LoungeSubScreen CreateLounge() => new TimeshiftLoungeSubScreen();
|
||||||
|
|
||||||
|
protected override OsuButton CreateNewMultiplayerGameButton() => new CreateTimeshiftRoomButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,11 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public const Easing FADE_EASING = Easing.Out;
|
public const Easing FADE_EASING = Easing.Out;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total height of all the top of screen scoring elements.
|
||||||
|
/// </summary>
|
||||||
|
public float TopScoringElementsHeight { get; private set; }
|
||||||
|
|
||||||
public readonly KeyCounterDisplay KeyCounter;
|
public readonly KeyCounterDisplay KeyCounter;
|
||||||
public readonly SkinnableComboCounter ComboCounter;
|
public readonly SkinnableComboCounter ComboCounter;
|
||||||
public readonly SkinnableScoreCounter ScoreCounter;
|
public readonly SkinnableScoreCounter ScoreCounter;
|
||||||
@ -209,7 +214,7 @@ namespace osu.Game.Screens.Play
|
|||||||
// HACK: for now align with the accuracy counter.
|
// HACK: for now align with the accuracy counter.
|
||||||
// this is done for the sake of hacky legacy skins which extend the health bar to take up the full screen area.
|
// this is done for the sake of hacky legacy skins which extend the health bar to take up the full screen area.
|
||||||
// it only works with the default skin due to padding offsetting it *just enough* to coexist.
|
// it only works with the default skin due to padding offsetting it *just enough* to coexist.
|
||||||
topRightElements.Y = ToLocalSpace(AccuracyCounter.Drawable.ScreenSpaceDrawQuad.BottomRight).Y;
|
topRightElements.Y = TopScoringElementsHeight = ToLocalSpace(AccuracyCounter.Drawable.ScreenSpaceDrawQuad.BottomRight).Y;
|
||||||
|
|
||||||
bottomRightElements.Y = -Progress.Height;
|
bottomRightElements.Y = -Progress.Height;
|
||||||
}
|
}
|
||||||
|
@ -374,7 +374,7 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!this.IsCurrentScreen()) return;
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
fadeOut(true);
|
fadeOut(true);
|
||||||
performImmediateExit();
|
PerformExit(true);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
||||||
@ -463,20 +463,30 @@ namespace osu.Game.Screens.Play
|
|||||||
return playable;
|
return playable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performImmediateExit()
|
/// <summary>
|
||||||
|
/// Exits the <see cref="Player"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userRequested">
|
||||||
|
/// Whether the exit is requested by the user, or a higher-level game component.
|
||||||
|
/// Pausing is allowed only in the former case.
|
||||||
|
/// </param>
|
||||||
|
protected void PerformExit(bool userRequested)
|
||||||
{
|
{
|
||||||
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
||||||
completionProgressDelegate?.Cancel();
|
completionProgressDelegate?.Cancel();
|
||||||
|
|
||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
|
|
||||||
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
|
if (userRequested)
|
||||||
performUserRequestedExit();
|
performUserRequestedExit();
|
||||||
|
else
|
||||||
|
this.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performUserRequestedExit()
|
private void performUserRequestedExit()
|
||||||
{
|
{
|
||||||
if (!this.IsCurrentScreen()) return;
|
|
||||||
|
|
||||||
if (ValidForResume && HasFailed && !FailOverlay.IsPresent)
|
if (ValidForResume && HasFailed && !FailOverlay.IsPresent)
|
||||||
{
|
{
|
||||||
failAnimation.FinishTransforms(true);
|
failAnimation.FinishTransforms(true);
|
||||||
@ -506,7 +516,7 @@ namespace osu.Game.Screens.Play
|
|||||||
RestartRequested?.Invoke();
|
RestartRequested?.Invoke();
|
||||||
|
|
||||||
if (this.IsCurrentScreen())
|
if (this.IsCurrentScreen())
|
||||||
performImmediateExit();
|
PerformExit(true);
|
||||||
else
|
else
|
||||||
this.MakeCurrent();
|
this.MakeCurrent();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user