2020-12-20 23:04:06 +08:00
|
|
|
// 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.
|
|
|
|
|
2020-12-29 15:20:43 +08:00
|
|
|
using System;
|
2020-12-20 23:04:06 +08:00
|
|
|
using System.Collections.Specialized;
|
2020-12-24 09:38:53 +08:00
|
|
|
using System.Diagnostics;
|
2020-12-20 23:04:06 +08:00
|
|
|
using System.Linq;
|
2020-12-29 15:20:43 +08:00
|
|
|
using JetBrains.Annotations;
|
2020-12-20 23:04:06 +08:00
|
|
|
using osu.Framework.Allocation;
|
2020-12-23 14:58:50 +08:00
|
|
|
using osu.Framework.Bindables;
|
2020-12-20 23:04:06 +08:00
|
|
|
using osu.Framework.Graphics;
|
|
|
|
using osu.Framework.Graphics.Containers;
|
|
|
|
using osu.Framework.Screens;
|
2020-12-31 01:00:57 +08:00
|
|
|
using osu.Game.Extensions;
|
2020-12-20 23:04:06 +08:00
|
|
|
using osu.Game.Online.Multiplayer;
|
2020-12-25 12:38:11 +08:00
|
|
|
using osu.Game.Online.Rooms;
|
2021-02-01 12:40:59 +08:00
|
|
|
using osu.Game.Overlays.Mods;
|
2020-12-25 23:50:00 +08:00
|
|
|
using osu.Game.Screens.OnlinePlay.Components;
|
|
|
|
using osu.Game.Screens.OnlinePlay.Match;
|
|
|
|
using osu.Game.Screens.OnlinePlay.Match.Components;
|
|
|
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
|
|
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
2021-02-01 12:40:59 +08:00
|
|
|
using osu.Game.Screens.Play.HUD;
|
2020-12-20 23:04:06 +08:00
|
|
|
using osu.Game.Users;
|
2021-02-01 12:40:59 +08:00
|
|
|
using osuTK;
|
2020-12-25 23:50:00 +08:00
|
|
|
using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.ParticipantsList;
|
2020-12-20 23:04:06 +08:00
|
|
|
|
2020-12-25 23:50:00 +08:00
|
|
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
|
|
|
[Cached]
|
2020-12-25 12:38:11 +08:00
|
|
|
public class MultiplayerMatchSubScreen : RoomSubScreen
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
|
|
|
public override string Title { get; }
|
|
|
|
|
2020-12-24 23:10:29 +08:00
|
|
|
public override string ShortTitle => "room";
|
2020-12-20 23:04:06 +08:00
|
|
|
|
|
|
|
[Resolved]
|
|
|
|
private StatefulMultiplayerClient client { get; set; }
|
|
|
|
|
2020-12-29 15:20:43 +08:00
|
|
|
[Resolved]
|
|
|
|
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
2020-12-29 03:59:38 +08:00
|
|
|
|
2021-02-01 12:40:59 +08:00
|
|
|
private ModSelectOverlay extraModSelectOverlay;
|
2020-12-25 12:38:11 +08:00
|
|
|
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
2021-02-01 12:40:59 +08:00
|
|
|
private Drawable extraModsSection;
|
2020-12-20 23:04:06 +08:00
|
|
|
|
2020-12-23 14:58:50 +08:00
|
|
|
private IBindable<bool> isConnected;
|
|
|
|
|
2020-12-29 15:20:43 +08:00
|
|
|
[CanBeNull]
|
2020-12-31 01:00:57 +08:00
|
|
|
private IDisposable readyClickOperation;
|
2020-12-29 15:20:43 +08:00
|
|
|
|
2021-01-19 21:52:43 +08:00
|
|
|
private GridContainer mainContent;
|
|
|
|
|
2020-12-25 12:38:11 +08:00
|
|
|
public MultiplayerMatchSubScreen(Room room)
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
2020-12-24 23:10:29 +08:00
|
|
|
Title = room.RoomID.Value == null ? "New room" : room.Name.Value;
|
2020-12-20 23:04:06 +08:00
|
|
|
Activity.Value = new UserActivity.InLobby(room);
|
|
|
|
}
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
private void load()
|
|
|
|
{
|
|
|
|
InternalChildren = new Drawable[]
|
|
|
|
{
|
2021-01-19 21:52:43 +08:00
|
|
|
mainContent = new GridContainer
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Content = new[]
|
|
|
|
{
|
|
|
|
new Drawable[]
|
|
|
|
{
|
|
|
|
new Container
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Padding = new MarginPadding
|
|
|
|
{
|
|
|
|
Horizontal = 105,
|
|
|
|
Vertical = 20
|
|
|
|
},
|
|
|
|
Child = new GridContainer
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
RowDimensions = new[]
|
|
|
|
{
|
|
|
|
new Dimension(GridSizeMode.AutoSize),
|
|
|
|
new Dimension(),
|
|
|
|
},
|
|
|
|
Content = new[]
|
|
|
|
{
|
|
|
|
new Drawable[]
|
|
|
|
{
|
2020-12-25 12:38:11 +08:00
|
|
|
new MultiplayerMatchHeader
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
|
|
|
OpenSettings = () => settingsOverlay.Show()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
new Drawable[]
|
|
|
|
{
|
|
|
|
new GridContainer
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Content = new[]
|
|
|
|
{
|
|
|
|
new Drawable[]
|
|
|
|
{
|
|
|
|
new Container
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
|
|
|
|
Child = new GridContainer
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
RowDimensions = new[]
|
|
|
|
{
|
|
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
|
|
},
|
|
|
|
Content = new[]
|
|
|
|
{
|
|
|
|
new Drawable[] { new ParticipantsListHeader() },
|
|
|
|
new Drawable[]
|
|
|
|
{
|
2020-12-23 15:19:03 +08:00
|
|
|
new ParticipantsList
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
new FillFlowContainer
|
|
|
|
{
|
|
|
|
Anchor = Anchor.Centre,
|
|
|
|
Origin = Anchor.Centre,
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
Padding = new MarginPadding { Horizontal = 5 },
|
2021-02-01 12:40:59 +08:00
|
|
|
Spacing = new Vector2(0, 10),
|
|
|
|
Children = new[]
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
2021-02-01 12:40:59 +08:00
|
|
|
new FillFlowContainer
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
Children = new Drawable[]
|
|
|
|
{
|
|
|
|
new OverlinedHeader("Beatmap"),
|
|
|
|
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
|
|
|
|
}
|
|
|
|
},
|
|
|
|
extraModsSection = new FillFlowContainer
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
AutoSizeAxes = Axes.Y,
|
|
|
|
Children = new Drawable[]
|
|
|
|
{
|
|
|
|
new OverlinedHeader("Extra mods"),
|
|
|
|
new ModDisplay
|
|
|
|
{
|
|
|
|
DisplayUnrankedText = false,
|
|
|
|
Current = ExtraMods
|
|
|
|
},
|
|
|
|
new PurpleTriangleButton
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.X,
|
|
|
|
Text = "Select",
|
|
|
|
Action = () => extraModSelectOverlay.Show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-20 23:04:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
new Drawable[]
|
|
|
|
{
|
|
|
|
new GridContainer
|
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
RowDimensions = new[]
|
|
|
|
{
|
|
|
|
new Dimension(GridSizeMode.AutoSize)
|
|
|
|
},
|
|
|
|
Content = new[]
|
|
|
|
{
|
|
|
|
new Drawable[] { new OverlinedHeader("Chat") },
|
|
|
|
new Drawable[] { new MatchChatDisplay { RelativeSizeAxes = Axes.Both } }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
new Drawable[]
|
|
|
|
{
|
2020-12-29 14:51:46 +08:00
|
|
|
new MultiplayerMatchFooter
|
|
|
|
{
|
|
|
|
SelectedItem = { BindTarget = SelectedItem },
|
2020-12-31 01:00:57 +08:00
|
|
|
OnReadyClick = onReadyClick
|
2020-12-29 14:51:46 +08:00
|
|
|
}
|
2020-12-20 23:04:06 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
RowDimensions = new[]
|
|
|
|
{
|
|
|
|
new Dimension(),
|
|
|
|
new Dimension(GridSizeMode.AutoSize),
|
|
|
|
}
|
|
|
|
},
|
2021-02-01 12:40:59 +08:00
|
|
|
extraModSelectOverlay = new SoloModSelectOverlay
|
|
|
|
{
|
|
|
|
SelectedMods = { BindTarget = ExtraMods },
|
|
|
|
Stacked = false,
|
|
|
|
IsValidMod = _ => false
|
|
|
|
},
|
2020-12-25 12:38:11 +08:00
|
|
|
settingsOverlay = new MultiplayerMatchSettingsOverlay
|
2020-12-20 23:04:06 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden }
|
|
|
|
}
|
|
|
|
};
|
2021-01-19 05:08:06 +08:00
|
|
|
|
2021-01-19 21:52:43 +08:00
|
|
|
if (client.Room == null)
|
|
|
|
{
|
2021-01-19 22:16:39 +08:00
|
|
|
// A new room is being created.
|
|
|
|
// The main content should be hidden until the settings overlay is hidden, signaling the room is ready to be displayed.
|
2021-01-19 21:52:43 +08:00
|
|
|
mainContent.Hide();
|
|
|
|
|
|
|
|
settingsOverlay.State.BindValueChanged(visibility =>
|
|
|
|
{
|
|
|
|
if (visibility.NewValue == Visibility.Hidden)
|
|
|
|
mainContent.Show();
|
|
|
|
}, true);
|
|
|
|
}
|
2020-12-20 23:04:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
|
|
|
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
|
|
|
|
|
|
|
client.LoadRequested += onLoadRequested;
|
2020-12-23 14:58:50 +08:00
|
|
|
|
|
|
|
isConnected = client.IsConnected.GetBoundCopy();
|
|
|
|
isConnected.BindValueChanged(connected =>
|
|
|
|
{
|
|
|
|
if (!connected.NewValue)
|
|
|
|
Schedule(this.Exit);
|
|
|
|
}, true);
|
2020-12-20 23:04:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnBackButton()
|
|
|
|
{
|
|
|
|
if (client.Room != null && settingsOverlay.State.Value == Visibility.Visible)
|
|
|
|
{
|
|
|
|
settingsOverlay.Hide();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:59 +08:00
|
|
|
if (extraModSelectOverlay.State.Value == Visibility.Visible)
|
|
|
|
{
|
|
|
|
extraModSelectOverlay.Hide();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-20 23:04:06 +08:00
|
|
|
return base.OnBackButton();
|
|
|
|
}
|
|
|
|
|
2021-02-01 12:40:59 +08:00
|
|
|
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
{
|
|
|
|
SelectedItem.Value = Playlist.FirstOrDefault();
|
|
|
|
|
|
|
|
if (SelectedItem.Value?.AllowedMods.Any() != true)
|
|
|
|
{
|
|
|
|
extraModsSection.Hide();
|
|
|
|
extraModSelectOverlay.Hide();
|
|
|
|
extraModSelectOverlay.IsValidMod = _ => false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
extraModsSection.Show();
|
|
|
|
extraModSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType());
|
|
|
|
}
|
|
|
|
}
|
2020-12-20 23:04:06 +08:00
|
|
|
|
2020-12-31 01:00:57 +08:00
|
|
|
private void onReadyClick()
|
2020-12-29 14:51:46 +08:00
|
|
|
{
|
2020-12-31 01:00:57 +08:00
|
|
|
Debug.Assert(readyClickOperation == null);
|
|
|
|
readyClickOperation = ongoingOperationTracker.BeginOperation();
|
|
|
|
|
|
|
|
if (client.IsHost && client.LocalUser?.State == MultiplayerUserState.Ready)
|
|
|
|
{
|
|
|
|
client.StartMatch()
|
|
|
|
.ContinueWith(t =>
|
|
|
|
{
|
|
|
|
// accessing Exception here silences any potential errors from the antecedent task
|
|
|
|
if (t.Exception != null)
|
|
|
|
{
|
2020-12-31 18:50:59 +08:00
|
|
|
t.CatchUnobservedExceptions(true); // will run immediately.
|
2020-12-31 01:00:57 +08:00
|
|
|
// gameplay was not started due to an exception; unblock button.
|
|
|
|
endOperation();
|
|
|
|
}
|
|
|
|
|
|
|
|
// gameplay is starting, the button will be unblocked on load requested.
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2020-12-29 16:09:47 +08:00
|
|
|
|
|
|
|
client.ToggleReady()
|
2020-12-31 18:50:59 +08:00
|
|
|
.ContinueWith(t =>
|
|
|
|
{
|
|
|
|
t.CatchUnobservedExceptions(true); // will run immediately.
|
|
|
|
endOperation();
|
|
|
|
});
|
2020-12-31 01:00:57 +08:00
|
|
|
|
|
|
|
void endOperation()
|
|
|
|
{
|
2021-01-13 07:58:53 +08:00
|
|
|
readyClickOperation?.Dispose();
|
2020-12-31 01:00:57 +08:00
|
|
|
readyClickOperation = null;
|
|
|
|
}
|
2020-12-29 14:51:46 +08:00
|
|
|
}
|
|
|
|
|
2020-12-24 09:38:53 +08:00
|
|
|
private void onLoadRequested()
|
|
|
|
{
|
|
|
|
Debug.Assert(client.Room != null);
|
|
|
|
|
2020-12-29 13:27:33 +08:00
|
|
|
int[] userIds = client.CurrentMatchPlayingUserIds.ToArray();
|
2020-12-24 09:38:53 +08:00
|
|
|
|
2020-12-25 12:38:11 +08:00
|
|
|
StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds));
|
2020-12-29 15:20:43 +08:00
|
|
|
|
2021-01-13 07:58:53 +08:00
|
|
|
readyClickOperation?.Dispose();
|
2020-12-31 01:00:57 +08:00
|
|
|
readyClickOperation = null;
|
2020-12-24 09:38:53 +08:00
|
|
|
}
|
2020-12-20 23:04:06 +08:00
|
|
|
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
|
|
{
|
|
|
|
base.Dispose(isDisposing);
|
|
|
|
|
|
|
|
if (client != null)
|
|
|
|
client.LoadRequested -= onLoadRequested;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|