mirror of
https://github.com/osukey/osukey.git
synced 2025-07-02 16:59:53 +09:00
Add a polling component model
This commit is contained in:
98
osu.Game.Tests/Visual/TestCasePollingComponent.cs
Normal file
98
osu.Game.Tests/Visual/TestCasePollingComponent.cs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
public class TestCasePollingComponent : OsuTestCase
|
||||||
|
{
|
||||||
|
private Container pollBox;
|
||||||
|
private TestPoller poller;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
poller = new TestPoller(),
|
||||||
|
pollBox = new Container
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(0.4f),
|
||||||
|
Colour = Color4.LimeGreen,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = "Poll!",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
poller.OnPoll += () =>
|
||||||
|
{
|
||||||
|
pollBox.FadeOutFromOne(500);
|
||||||
|
count++;
|
||||||
|
};
|
||||||
|
|
||||||
|
AddStep("set poll to 1 second", () => poller.TimeBetweenPolls = TimePerAction);
|
||||||
|
|
||||||
|
void checkCount(int checkValue) => AddAssert($"count is {checkValue}", () => count == checkValue);
|
||||||
|
|
||||||
|
checkCount(1);
|
||||||
|
checkCount(2);
|
||||||
|
checkCount(3);
|
||||||
|
|
||||||
|
AddStep("set poll to 5 second", () => poller.TimeBetweenPolls = TimePerAction * 5);
|
||||||
|
|
||||||
|
checkCount(4);
|
||||||
|
checkCount(4);
|
||||||
|
checkCount(4);
|
||||||
|
checkCount(4);
|
||||||
|
|
||||||
|
checkCount(5);
|
||||||
|
checkCount(5);
|
||||||
|
checkCount(5);
|
||||||
|
|
||||||
|
AddStep("set poll to 5 second", () => poller.TimeBetweenPolls = TimePerAction);
|
||||||
|
|
||||||
|
AddAssert("count is 6", () => count == 6);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double TimePerAction => 500;
|
||||||
|
|
||||||
|
public class TestPoller : PollingComponent
|
||||||
|
{
|
||||||
|
public event Action OnPoll;
|
||||||
|
|
||||||
|
protected override Task Poll()
|
||||||
|
{
|
||||||
|
OnPoll?.Invoke();
|
||||||
|
return base.Poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Threading;
|
|
||||||
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.Users;
|
using osu.Game.Users;
|
||||||
@ -18,7 +17,7 @@ namespace osu.Game.Online.Chat
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages everything channel related
|
/// Manages everything channel related
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChannelManager : Component, IOnlineComponent
|
public class ChannelManager : PollingComponent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The channels the player joins on startup
|
/// The channels the player joins on startup
|
||||||
@ -49,11 +48,14 @@ namespace osu.Game.Online.Chat
|
|||||||
public IBindableCollection<Channel> AvailableChannels => availableChannels;
|
public IBindableCollection<Channel> AvailableChannels => availableChannels;
|
||||||
|
|
||||||
private IAPIProvider api;
|
private IAPIProvider api;
|
||||||
private ScheduledDelegate fetchMessagesScheduleder;
|
|
||||||
|
public readonly BindableBool HighPollRate = new BindableBool();
|
||||||
|
|
||||||
public ChannelManager()
|
public ChannelManager()
|
||||||
{
|
{
|
||||||
CurrentChannel.ValueChanged += currentChannelChanged;
|
CurrentChannel.ValueChanged += currentChannelChanged;
|
||||||
|
|
||||||
|
HighPollRate.BindValueChanged(high => TimeBetweenPolls = high ? 1000 : 6000, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -360,73 +362,60 @@ namespace osu.Game.Online.Chat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void APIStateChanged(APIAccess api, APIState state)
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case APIState.Online:
|
|
||||||
fetchUpdates();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fetchMessagesScheduleder?.Cancel();
|
|
||||||
fetchMessagesScheduleder = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long lastMessageId;
|
private long lastMessageId;
|
||||||
private const int update_poll_interval = 1000;
|
|
||||||
|
|
||||||
private bool channelsInitialised;
|
private bool channelsInitialised;
|
||||||
|
|
||||||
private void fetchUpdates()
|
protected override Task Poll()
|
||||||
{
|
{
|
||||||
fetchMessagesScheduleder?.Cancel();
|
if (!api.IsLoggedIn)
|
||||||
fetchMessagesScheduleder = Scheduler.AddDelayed(() =>
|
return base.Poll();
|
||||||
|
|
||||||
|
var fetchReq = new GetUpdatesRequest(lastMessageId);
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
|
fetchReq.Success += updates =>
|
||||||
{
|
{
|
||||||
var fetchReq = new GetUpdatesRequest(lastMessageId);
|
if (updates?.Presence != null)
|
||||||
|
|
||||||
fetchReq.Success += updates =>
|
|
||||||
{
|
{
|
||||||
if (updates?.Presence != null)
|
foreach (var channel in updates.Presence)
|
||||||
{
|
{
|
||||||
foreach (var channel in updates.Presence)
|
// we received this from the server so should mark the channel already joined.
|
||||||
{
|
JoinChannel(channel, true);
|
||||||
// we received this from the server so should mark the channel already joined.
|
|
||||||
JoinChannel(channel, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: handle left channels
|
|
||||||
|
|
||||||
handleChannelMessages(updates.Messages);
|
|
||||||
|
|
||||||
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
|
|
||||||
JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
|
||||||
|
|
||||||
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!channelsInitialised)
|
//todo: handle left channels
|
||||||
{
|
|
||||||
channelsInitialised = true;
|
|
||||||
// we want this to run after the first presence so we can see if the user is in any channels already.
|
|
||||||
initializeChannels();
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchUpdates();
|
handleChannelMessages(updates.Messages);
|
||||||
};
|
|
||||||
|
|
||||||
fetchReq.Failure += delegate { fetchUpdates(); };
|
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
|
||||||
|
JoinedChannels.FirstOrDefault(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
||||||
|
|
||||||
api.Queue(fetchReq);
|
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
|
||||||
}, update_poll_interval);
|
}
|
||||||
|
|
||||||
|
if (!channelsInitialised)
|
||||||
|
{
|
||||||
|
channelsInitialised = true;
|
||||||
|
// we want this to run after the first presence so we can see if the user is in any channels already.
|
||||||
|
initializeChannels();
|
||||||
|
}
|
||||||
|
|
||||||
|
tcs.SetResult(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchReq.Failure += _ => tcs.SetResult(false);
|
||||||
|
|
||||||
|
api.Queue(fetchReq);
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IAPIProvider api)
|
private void load(IAPIProvider api)
|
||||||
{
|
{
|
||||||
this.api = api;
|
this.api = api;
|
||||||
api.Register(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
osu.Game/Online/PollingComponent.cs
Normal file
108
osu.Game/Online/PollingComponent.cs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
|
||||||
|
namespace osu.Game.Online
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A component which requires a constant polling process.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class PollingComponent : Component
|
||||||
|
{
|
||||||
|
private double? lastTimePolled;
|
||||||
|
|
||||||
|
private ScheduledDelegate scheduledPoll;
|
||||||
|
|
||||||
|
private bool pollingActive;
|
||||||
|
|
||||||
|
private double timeBetweenPolls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time that should be waited between polls.
|
||||||
|
/// </summary>
|
||||||
|
public double TimeBetweenPolls
|
||||||
|
{
|
||||||
|
get => timeBetweenPolls;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
timeBetweenPolls = value;
|
||||||
|
scheduledPoll?.Cancel();
|
||||||
|
pollIfNecessary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
pollIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool pollIfNecessary()
|
||||||
|
{
|
||||||
|
// we must be loaded so we have access to clock.
|
||||||
|
if (!IsLoaded) return false;
|
||||||
|
|
||||||
|
// there's already a poll process running.
|
||||||
|
if (pollingActive) return false;
|
||||||
|
|
||||||
|
// don't try polling if the time between polls hasn't been set.
|
||||||
|
if (timeBetweenPolls == 0) return false;
|
||||||
|
|
||||||
|
if (!lastTimePolled.HasValue)
|
||||||
|
{
|
||||||
|
doPoll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Time.Current - lastTimePolled.Value > timeBetweenPolls)
|
||||||
|
{
|
||||||
|
doPoll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not ennough time has passed since the last poll. we do want to schedule a poll to happen, though.
|
||||||
|
scheduleNextPoll();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doPoll()
|
||||||
|
{
|
||||||
|
scheduledPoll = null;
|
||||||
|
pollingActive = true;
|
||||||
|
Poll().ContinueWith(_ => pollComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform the polling in this method. Call <see cref="pollComplete"/> when done.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual Task Poll()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call when a poll operation has completed.
|
||||||
|
/// </summary>
|
||||||
|
private void pollComplete()
|
||||||
|
{
|
||||||
|
lastTimePolled = Time.Current;
|
||||||
|
pollingActive = false;
|
||||||
|
|
||||||
|
if (scheduledPoll == null)
|
||||||
|
scheduleNextPoll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleNextPoll()
|
||||||
|
{
|
||||||
|
scheduledPoll?.Cancel();
|
||||||
|
|
||||||
|
double lastPollDuration = lastTimePolled.HasValue ? Time.Current - lastTimePolled.Value : 0;
|
||||||
|
|
||||||
|
scheduledPoll = Scheduler.AddDelayed(doPoll, Math.Max(0, timeBetweenPolls - lastPollDuration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -418,6 +418,8 @@ namespace osu.Game
|
|||||||
dependencies.Cache(notifications);
|
dependencies.Cache(notifications);
|
||||||
dependencies.Cache(dialogOverlay);
|
dependencies.Cache(dialogOverlay);
|
||||||
|
|
||||||
|
chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible;
|
||||||
|
|
||||||
Add(externalLinkOpener = new ExternalLinkOpener());
|
Add(externalLinkOpener = new ExternalLinkOpener());
|
||||||
|
|
||||||
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
|
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
|
||||||
|
Reference in New Issue
Block a user