1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-13 07:17:28 +08:00

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

445 lines
16 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.
2018-05-22 00:07:04 -03:00
2020-12-29 08:20:43 +01:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
2019-02-08 15:20:11 +09:00
using osu.Framework.Allocation;
2019-02-21 19:04:31 +09:00
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
2018-05-22 00:07:04 -03:00
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
2018-05-22 00:07:04 -03:00
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
2018-10-02 12:02:47 +09:00
using osu.Framework.Input.Events;
using osu.Framework.Logging;
2018-05-22 00:24:39 -03:00
using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Online.API;
2020-12-25 13:38:11 +09:00
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
2018-05-22 00:07:04 -03:00
namespace osu.Game.Screens.OnlinePlay.Lounge
2018-05-22 00:07:04 -03:00
{
[Cached]
2025-01-23 16:19:09 +09:00
[Cached(typeof(IOnlinePlayLounge))]
public abstract partial class LoungeSubScreen : OnlinePlaySubScreen, IOnlinePlayLounge
2018-05-22 00:07:04 -03:00
{
2019-01-25 19:32:37 +09:00
public override string Title => "Lounge";
2021-08-20 21:40:35 +09:00
protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen
{
SelectedRoom = { BindTarget = roomListing.SelectedRoom }
2021-08-20 21:40:35 +09:00
};
protected override UserActivity InitialActivity => new UserActivity.SearchingForLobby();
protected Container<OsuButton> Buttons { get; } = new Container<OsuButton>
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both
};
2020-08-11 12:40:58 +09:00
[Resolved]
private MusicController music { get; set; } = null!;
[Resolved(CanBeNull = true)]
private OngoingOperationTracker? ongoingOperationTracker { get; set; }
2020-12-29 08:20:43 +01:00
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
[Resolved(CanBeNull = true)]
private IdleTracker? idleTracker { get; set; }
[Resolved]
protected OsuConfigManager Config { get; private set; } = null!;
2025-02-12 18:35:35 +09:00
private IDisposable? joiningRoomOperation;
private readonly Bindable<FilterCriteria?> filter = new Bindable<FilterCriteria?>();
2025-02-12 18:35:35 +09:00
private readonly Bindable<bool> hasListingResults = new Bindable<bool>();
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
private readonly IBindable<bool> isIdle = new BindableBool();
private RoomListing roomListing = null!;
private LoungeListingPoller listingPoller = null!;
private PopoverContainer popoverContainer = null!;
private LoadingLayer loadingLayer = null!;
private SearchTextBox searchTextBox = null!;
protected Dropdown<RoomModeFilter> StatusDropdown { get; private set; } = null!;
[BackgroundDependencyLoader(true)]
private void load()
2018-05-22 00:07:04 -03:00
{
Masking = true;
const float controls_area_height = 25f;
if (idleTracker != null)
isIdle.BindTo(idleTracker.IsIdle);
Color4 bg = Color4Extensions.FromHex("#070405");
2021-07-14 18:24:30 +09:00
InternalChildren = new Drawable[]
2018-05-22 00:07:04 -03:00
{
listingPoller = new LoungeListingPoller
2025-02-12 18:35:35 +09:00
{
RoomsReceived = onListingReceived,
Filter = { BindTarget = filter }
},
popoverContainer = new PopoverContainer
2018-05-22 00:07:04 -03:00
{
Name = @"Rooms area",
2018-05-22 00:07:04 -03:00
RelativeSizeAxes = Axes.Both,
2021-07-14 18:24:30 +09:00
Padding = new MarginPadding
2018-05-22 00:07:04 -03:00
{
Horizontal = WaveOverlayContainer.WIDTH_PADDING,
Top = Header.HEIGHT + controls_area_height + 20,
2021-07-14 17:46:45 +09:00
},
Child = roomListing = new RoomListing
2021-07-14 17:46:45 +09:00
{
2021-07-14 18:24:30 +09:00
RelativeSizeAxes = Axes.Both,
Filter = { BindTarget = filter },
}
},
loadingLayer = new LoadingLayer(true),
new Container
{
Name = "Header area",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
2021-07-14 17:46:45 +09:00
{
new Box
2018-05-22 00:07:04 -03:00
{
Colour = ColourInfo.GradientVertical(bg, bg.Opacity(0.75f)),
RelativeSizeAxes = Axes.Both,
Height = 0.8f,
},
new Box
{
Colour = ColourInfo.GradientVertical(bg.Opacity(0.75f), bg.Opacity(0)),
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Y = 0.8f,
// Intentionally taller than the header for a more gradual fade
Height = 0.5f,
2021-07-14 17:46:45 +09:00
},
new FillFlowContainer
2018-05-22 00:07:04 -03:00
{
Name = @"Header area flow",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.WIDTH_PADDING },
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = Header.HEIGHT,
Child = searchTextBox = new BasicSearchTextBox
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.X,
Width = 0.6f,
},
},
new Container
2021-07-14 17:46:45 +09:00
{
RelativeSizeAxes = Axes.X,
Height = controls_area_height,
Children = new Drawable[]
2021-07-14 17:46:45 +09:00
{
Buttons.WithChild(CreateNewRoomButton().With(d =>
{
d.Anchor = Anchor.BottomLeft;
d.Origin = Anchor.BottomLeft;
d.Size = new Vector2(150, 37.5f);
d.Action = () => Open();
})),
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10),
ChildrenEnumerable = CreateFilterControls().Select(f => f.With(d =>
{
d.Anchor = Anchor.TopRight;
d.Origin = Anchor.TopRight;
}))
}
}
}
},
},
}
},
2018-05-22 00:07:04 -03:00
};
}
protected override void LoadComplete()
{
base.LoadComplete();
searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced());
ruleset.BindValueChanged(_ => UpdateFilter());
isIdle.BindValueChanged(_ => updatePollingRate(this.IsCurrentScreen()), true);
2020-12-29 08:20:43 +01:00
if (ongoingOperationTracker != null)
{
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
operationInProgress.BindValueChanged(_ => updateLoadingLayer());
}
2025-02-12 18:35:35 +09:00
hasListingResults.BindValueChanged(_ => updateLoadingLayer());
filter.BindValueChanged(_ =>
{
roomListing.Rooms.Clear();
2025-02-12 18:35:35 +09:00
hasListingResults.Value = false;
listingPoller.PollImmediately();
2025-02-12 18:35:35 +09:00
});
updateFilter();
}
2025-02-12 18:35:35 +09:00
private void onListingReceived(Room[] result)
{
Dictionary<long, Room> localRoomsById = roomListing.Rooms.ToDictionary(r => r.RoomID!.Value);
2025-02-12 18:35:35 +09:00
Dictionary<long, Room> resultRoomsById = result.ToDictionary(r => r.RoomID!.Value);
// Remove all local rooms no longer in the result set.
roomListing.Rooms.RemoveAll(r => !resultRoomsById.ContainsKey(r.RoomID!.Value));
2025-02-12 18:35:35 +09:00
// Add or update local rooms with the result set.
foreach (var r in result)
{
if (localRoomsById.TryGetValue(r.RoomID!.Value, out Room? existingRoom))
existingRoom.CopyFrom(r);
else
roomListing.Rooms.Add(r);
2025-02-12 18:35:35 +09:00
}
hasListingResults.Value = true;
}
#region Filtering
2021-08-17 09:36:43 +09:00
public void UpdateFilter() => Scheduler.AddOnce(updateFilter);
private ScheduledDelegate? scheduledFilterUpdate;
private void updateFilterDebounced()
{
scheduledFilterUpdate?.Cancel();
scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
}
private void updateFilter()
{
scheduledFilterUpdate?.Cancel();
filter.Value = CreateFilterCriteria();
}
protected virtual FilterCriteria CreateFilterCriteria() => new FilterCriteria
{
SearchString = searchTextBox.Current.Value,
Ruleset = ruleset.Value,
Mode = StatusDropdown.Current.Value
};
protected virtual IEnumerable<Drawable> CreateFilterControls()
2018-05-22 00:24:39 -03:00
{
StatusDropdown = new SlimEnumDropdown<RoomModeFilter>
{
RelativeSizeAxes = Axes.None,
Width = 160,
};
StatusDropdown.Current.BindValueChanged(_ => UpdateFilter());
yield return StatusDropdown;
2018-05-22 00:24:39 -03:00
}
#endregion
public override void OnEntering(ScreenTransitionEvent e)
2018-05-22 00:24:39 -03:00
{
base.OnEntering(e);
onReturning();
}
public override void OnResuming(ScreenTransitionEvent e)
{
base.OnResuming(e);
music.EnsurePlayingSomething();
onReturning();
// Poll for any newly-created rooms (including potentially the user's own).
listingPoller.PollImmediately();
}
public override bool OnExiting(ScreenExitEvent e)
2018-05-22 00:24:39 -03:00
{
onLeaving();
return base.OnExiting(e);
}
public override void OnSuspending(ScreenTransitionEvent e)
{
onLeaving();
base.OnSuspending(e);
}
protected override void OnFocus(FocusEvent e)
{
searchTextBox.TakeFocus();
}
private void onReturning()
{
updatePollingRate(true);
searchTextBox.HoldFocus = true;
}
private void onLeaving()
{
updatePollingRate(false);
searchTextBox.HoldFocus = false;
// ensure any password prompt is dismissed.
popoverContainer.HidePopover();
2018-05-22 00:24:39 -03:00
}
public void Join(Room room, string? password, Action<Room>? onSuccess = null, Action<string>? onFailure = null) => Schedule(() =>
{
if (joiningRoomOperation != null)
return;
2020-12-29 08:20:43 +01:00
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
2025-01-25 19:27:21 +09:00
JoinInternal(room, password, r =>
{
2019-02-05 19:00:01 +09:00
Open(room);
2020-12-29 08:20:43 +01:00
joiningRoomOperation?.Dispose();
joiningRoomOperation = null;
onSuccess?.Invoke(room);
}, error =>
{
2020-12-29 08:20:43 +01:00
joiningRoomOperation?.Dispose();
joiningRoomOperation = null;
onFailure?.Invoke(error);
});
});
2025-01-25 19:27:21 +09:00
protected abstract void JoinInternal(Room room, string? password, Action<Room> onSuccess, Action<string> onFailure);
2025-01-25 19:27:21 +09:00
public void OpenCopy(Room room)
{
Debug.Assert(room.RoomID != null);
if (joiningRoomOperation != null)
return;
joiningRoomOperation = ongoingOperationTracker?.BeginOperation();
var req = new GetRoomRequest(room.RoomID.Value);
req.Success += r =>
{
// ID must be unset as we use this as a marker for whether this is a client-side (not-yet-created) room or not.
r.RoomID = null;
2022-03-15 11:30:57 +09:00
// Null out dates because end date is not supported client-side and the settings overlay will populate a duration.
r.EndDate = null;
r.Duration = null;
2022-03-15 11:30:57 +09:00
Open(r);
joiningRoomOperation?.Dispose();
joiningRoomOperation = null;
};
req.Failure += exception =>
{
Logger.Error(exception, "Couldn't create a copy of this room.");
joiningRoomOperation?.Dispose();
joiningRoomOperation = null;
};
api.Queue(req);
}
public abstract void Close(Room room);
2025-01-23 16:19:09 +09:00
2018-12-26 20:21:30 +09:00
/// <summary>
/// Push a room as a new subscreen.
/// </summary>
/// <param name="room">An optional template to use when creating the room.</param>
public void Open(Room? room = null) => Schedule(() =>
2018-12-04 15:26:06 +09:00
{
// Handles the case where a room is clicked 3 times in quick succession
2019-01-23 20:52:00 +09:00
if (!this.IsCurrentScreen())
2018-12-04 15:26:06 +09:00
return;
OpenNewRoom(room ?? CreateNewRoom());
});
protected virtual void OpenNewRoom(Room room) => this.Push(CreateRoomSubScreen(room));
2020-12-20 23:36:56 +09:00
public void RefreshRooms() => listingPoller.PollImmediately();
private void updateLoadingLayer()
{
2025-02-12 18:35:35 +09:00
if (operationInProgress.Value || !hasListingResults.Value)
loadingLayer.Show();
else
loadingLayer.Hide();
}
private void updatePollingRate(bool isCurrentScreen)
{
if (!isCurrentScreen)
listingPoller.TimeBetweenPolls.Value = 0;
else
listingPoller.TimeBetweenPolls.Value = isIdle.Value ? 120000 : 15000;
Logger.Log($"Polling adjusted (listing: {listingPoller.TimeBetweenPolls.Value})");
}
protected abstract OsuButton CreateNewRoomButton();
/// <summary>
/// Creates a new room.
/// </summary>
/// <returns>The created <see cref="Room"/>.</returns>
protected abstract Room CreateNewRoom();
2020-12-21 00:21:30 +09:00
protected abstract RoomSubScreen CreateRoomSubScreen(Room room);
2018-05-22 00:07:04 -03:00
}
}