1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-23 16:07:24 +08:00
osu-lazer/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs

398 lines
19 KiB
C#
Raw Normal View History

// 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;
2021-02-01 16:54:56 +08:00
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
2020-12-29 15:20:43 +08:00
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Online.Multiplayer;
2020-12-25 12:38:11 +08:00
using osu.Game.Online.Rooms;
using osu.Game.Overlays.Mods;
2021-02-01 16:54:56 +08:00
using osu.Game.Rulesets.Mods;
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;
using osu.Game.Screens.Play.HUD;
using osu.Game.Users;
using osuTK;
using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.ParticipantsList;
namespace osu.Game.Screens.OnlinePlay.Multiplayer
{
[Cached]
2020-12-25 12:38:11 +08:00
public class MultiplayerMatchSubScreen : RoomSubScreen
{
public override string Title { get; }
public override string ShortTitle => "room";
[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 16:57:32 +08:00
private ModSelectOverlay userModsSelectOverlay;
2020-12-25 12:38:11 +08:00
private MultiplayerMatchSettingsOverlay settingsOverlay;
2021-02-01 16:57:32 +08:00
private Drawable userModsSection;
private IBindable<bool> isConnected;
2020-12-29 15:20:43 +08:00
[CanBeNull]
private IDisposable readyClickOperation;
2020-12-29 15:20:43 +08:00
private GridContainer mainContent;
2020-12-25 12:38:11 +08:00
public MultiplayerMatchSubScreen(Room room)
{
Title = room.RoomID.Value == null ? "New room" : room.Name.Value;
Activity.Value = new UserActivity.InLobby(room);
}
[BackgroundDependencyLoader]
private void load()
{
AddRangeInternal(new Drawable[]
{
mainContent = new GridContainer
{
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
{
OpenSettings = () => settingsOverlay.Show()
}
},
new Drawable[]
{
2021-02-03 13:52:36 +08:00
new Container
{
RelativeSizeAxes = Axes.Both,
2021-02-03 13:52:36 +08:00
Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
Child = new GridContainer
{
2021-02-03 13:52:36 +08:00
RelativeSizeAxes = Axes.Both,
2021-02-05 12:05:11 +08:00
ColumnDimensions = new[]
{
2021-02-05 12:05:11 +08:00
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400),
new Dimension(),
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600),
},
2021-02-03 13:52:36 +08:00
Content = new[]
{
2021-02-03 13:52:36 +08:00
new Drawable[]
{
2021-02-03 13:52:36 +08:00
// Main left column
new GridContainer
{
RelativeSizeAxes = Axes.Both,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[] { new ParticipantsListHeader() },
new Drawable[]
{
new ParticipantsList
{
RelativeSizeAxes = Axes.Both
},
}
}
2021-02-03 13:52:36 +08:00
},
2021-02-05 12:05:11 +08:00
// Spacer
null,
2021-02-03 13:52:36 +08:00
// Main right column
new FillFlowContainer
{
2021-02-03 13:52:36 +08:00
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
2021-02-03 13:52:36 +08:00
new FillFlowContainer
{
2021-02-03 13:52:36 +08:00
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
2021-02-03 13:52:36 +08:00
new OverlinedHeader("Beatmap"),
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
}
},
userModsSection = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
2021-02-03 13:52:36 +08:00
new OverlinedHeader("Extra mods"),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new PurpleTriangleButton
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = 90,
Text = "Select",
Action = () => userModsSelectOverlay.Show()
},
new ModDisplay
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
DisplayUnrankedText = false,
Current = UserMods,
Scale = new Vector2(0.8f),
},
}
}
}
}
}
}
}
}
}
}
},
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[]
{
new MultiplayerMatchFooter
{
OnReadyClick = onReadyClick
}
}
},
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
}
},
2021-02-01 17:18:59 +08:00
new Container
{
2021-02-01 17:18:59 +08:00
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
2021-02-01 17:54:47 +08:00
Child = userModsSelectOverlay = new UserModSelectOverlay
2021-02-01 17:18:59 +08:00
{
SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false
}
},
2020-12-25 12:38:11 +08:00
settingsOverlay = new MultiplayerMatchSettingsOverlay
{
RelativeSizeAxes = Axes.Both,
State = { Value = client.Room == null ? Visibility.Visible : Visibility.Hidden }
}
});
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.
mainContent.Hide();
settingsOverlay.State.BindValueChanged(visibility =>
{
if (visibility.NewValue == Visibility.Hidden)
mainContent.Show();
}, true);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
Playlist.BindCollectionChanged(onPlaylistChanged, true);
BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true);
2021-02-01 16:57:32 +08:00
UserMods.BindValueChanged(onUserModsChanged);
client.LoadRequested += onLoadRequested;
isConnected = client.IsConnected.GetBoundCopy();
isConnected.BindValueChanged(connected =>
{
if (!connected.NewValue)
Schedule(this.Exit);
}, true);
}
public override bool OnBackButton()
{
if (client.Room != null && settingsOverlay.State.Value == Visibility.Visible)
{
settingsOverlay.Hide();
return true;
}
2021-02-01 16:57:32 +08:00
if (userModsSelectOverlay.State.Value == Visibility.Visible)
{
2021-02-01 16:57:32 +08:00
userModsSelectOverlay.Hide();
return true;
}
return base.OnBackButton();
}
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SelectedItem.Value = Playlist.FirstOrDefault();
if (SelectedItem.Value?.AllowedMods.Any() != true)
{
2021-02-01 16:57:32 +08:00
userModsSection.Hide();
userModsSelectOverlay.Hide();
userModsSelectOverlay.IsValidMod = _ => false;
}
else
{
2021-02-01 16:57:32 +08:00
userModsSection.Show();
userModsSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType());
}
}
2021-02-01 16:57:32 +08:00
private void onUserModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
2021-02-01 16:54:56 +08:00
{
if (client.Room == null)
return;
2021-02-01 18:28:33 +08:00
client.ChangeUserMods(mods.NewValue);
2021-02-01 16:54:56 +08:00
}
private void updateBeatmapAvailability(ValueChangedEvent<BeatmapAvailability> availability)
{
if (client.Room == null)
return;
client.ChangeBeatmapAvailability(availability.NewValue);
2021-02-05 15:19:45 +08:00
// while this flow is handled server-side, this covers the edge case of the local user being in a ready state and then deleting the current beatmap.
if (client.LocalUser?.State == MultiplayerUserState.Ready)
client.ChangeState(MultiplayerUserState.Idle);
}
private void onReadyClick()
{
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)
{
// gameplay was not started due to an exception; unblock button.
endOperation();
}
// gameplay is starting, the button will be unblocked on load requested.
});
return;
}
client.ToggleReady()
.ContinueWith(t => endOperation());
void endOperation()
{
readyClickOperation?.Dispose();
readyClickOperation = null;
}
}
private void onLoadRequested()
{
Debug.Assert(client.Room != null);
int[] userIds = client.CurrentMatchPlayingUserIds.ToArray();
2020-12-25 12:38:11 +08:00
StartPlay(() => new MultiplayerPlayer(SelectedItem.Value, userIds));
2020-12-29 15:20:43 +08:00
readyClickOperation?.Dispose();
readyClickOperation = null;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (client != null)
client.LoadRequested -= onLoadRequested;
}
2021-02-01 17:54:47 +08:00
private class UserModSelectOverlay : ModSelectOverlay
{
public UserModSelectOverlay()
{
CustomiseButton.Alpha = 0;
}
2021-02-01 17:54:47 +08:00
}
}
}