1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 10:02:59 +08:00

Update existing subscriptions to new style

Fix missing detach calls in `MusicController`
This commit is contained in:
Dean Herbert 2022-01-21 19:39:49 +09:00
parent 45aea9add5
commit 1f157d729d
14 changed files with 98 additions and 84 deletions

View File

@ -46,7 +46,7 @@ namespace osu.Game.Tests.Database
{ {
bool callbackRan = false; bool callbackRan = false;
realmFactory.Run(realm => realmFactory.Register(realm =>
{ {
var subscription = realm.All<BeatmapInfo>().QueryAsyncWithNotifications((sender, changes, error) => var subscription = realm.All<BeatmapInfo>().QueryAsyncWithNotifications((sender, changes, error) =>
{ {
@ -60,6 +60,7 @@ namespace osu.Game.Tests.Database
realmFactory.Run(r => r.Refresh()); realmFactory.Run(r => r.Refresh());
subscription?.Dispose(); subscription?.Dispose();
return null;
}); });
Assert.IsTrue(callbackRan); Assert.IsTrue(callbackRan);

View File

@ -234,7 +234,7 @@ namespace osu.Game.Tests.Database
{ {
int changesTriggered = 0; int changesTriggered = 0;
realmFactory.Run(outerRealm => realmFactory.Register(outerRealm =>
{ {
outerRealm.All<BeatmapInfo>().QueryAsyncWithNotifications(gotChange); outerRealm.All<BeatmapInfo>().QueryAsyncWithNotifications(gotChange);
ILive<BeatmapInfo>? liveBeatmap = null; ILive<BeatmapInfo>? liveBeatmap = null;
@ -277,6 +277,8 @@ namespace osu.Game.Tests.Database
r.Remove(resolved); r.Remove(resolved);
}); });
}); });
return null;
}); });
void gotChange(IRealmCollection<BeatmapInfo> sender, ChangeSet changes, Exception error) void gotChange(IRealmCollection<BeatmapInfo> sender, ChangeSet changes, Exception error)

View File

@ -244,7 +244,6 @@ namespace osu.Game.Database
if (!ThreadSafety.IsUpdateThread) if (!ThreadSafety.IsUpdateThread)
throw new InvalidOperationException(@$"{nameof(Register)} must be called from the update thread."); throw new InvalidOperationException(@$"{nameof(Register)} must be called from the update thread.");
subscriptionActions.Add(action, null);
registerSubscription(action); registerSubscription(action);
return new InvokeOnDisposal(() => return new InvokeOnDisposal(() =>
@ -264,10 +263,8 @@ namespace osu.Game.Database
lock (contextLock) lock (contextLock)
{ {
Debug.Assert(context != null);
current_thread_subscriptions_allowed.Value = true; current_thread_subscriptions_allowed.Value = true;
subscriptionActions[action] = action(context); subscriptionActions[action] = action(Context);
current_thread_subscriptions_allowed.Value = false; current_thread_subscriptions_allowed.Value = false;
} }
} }

View File

@ -23,7 +23,6 @@ namespace osu.Game.Input.Bindings
private readonly int? variant; private readonly int? variant;
private IDisposable realmSubscription; private IDisposable realmSubscription;
private IQueryable<RealmKeyBinding> realmKeyBindings;
[Resolved] [Resolved]
private RealmContextFactory realmFactory { get; set; } private RealmContextFactory realmFactory { get; set; }
@ -47,22 +46,25 @@ namespace osu.Game.Input.Bindings
throw new InvalidOperationException($"{nameof(variant)} can not be null when a non-null {nameof(ruleset)} is provided."); throw new InvalidOperationException($"{nameof(variant)} can not be null when a non-null {nameof(ruleset)} is provided.");
} }
private IQueryable<RealmKeyBinding> realmKeyBindings
{
get
{
string rulesetName = ruleset?.ShortName;
return realmFactory.Context.All<RealmKeyBinding>()
.Where(b => b.RulesetName == rulesetName && b.Variant == variant);
}
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
string rulesetName = ruleset?.ShortName; realmSubscription = realmFactory.Register(realm => realmKeyBindings
realmKeyBindings = realmFactory.Context.All<RealmKeyBinding>()
.Where(b => b.RulesetName == rulesetName && b.Variant == variant);
realmSubscription = realmKeyBindings
.QueryAsyncWithNotifications((sender, changes, error) => .QueryAsyncWithNotifications((sender, changes, error) =>
{ {
// first subscription ignored as we are handling this in LoadComplete. // The first fire of this is a bit redundant as this is being called in base.LoadComplete,
if (changes == null) // but this is safest in case the subscription is restored after a context recycle.
return;
ReloadMappings(); ReloadMappings();
}); }));
base.LoadComplete(); base.LoadComplete();
} }

View File

@ -42,7 +42,7 @@ namespace osu.Game.Online
// Used to interact with manager classes that don't support interface types. Will eventually be replaced. // Used to interact with manager classes that don't support interface types. Will eventually be replaced.
var beatmapSetInfo = new BeatmapSetInfo { OnlineID = TrackedItem.OnlineID }; var beatmapSetInfo = new BeatmapSetInfo { OnlineID = TrackedItem.OnlineID };
realmSubscription = realmContextFactory.Context.All<BeatmapSetInfo>().Where(s => s.OnlineID == TrackedItem.OnlineID && !s.DeletePending).QueryAsyncWithNotifications((items, changes, ___) => realmSubscription = realmContextFactory.Register(realm => realm.All<BeatmapSetInfo>().Where(s => s.OnlineID == TrackedItem.OnlineID && !s.DeletePending).QueryAsyncWithNotifications((items, changes, ___) =>
{ {
if (items.Any()) if (items.Any())
Schedule(() => UpdateState(DownloadState.LocallyAvailable)); Schedule(() => UpdateState(DownloadState.LocallyAvailable));
@ -54,7 +54,7 @@ namespace osu.Game.Online
attachDownload(Downloader.GetExistingDownload(beatmapSetInfo)); attachDownload(Downloader.GetExistingDownload(beatmapSetInfo));
}); });
} }
}); }));
} }
private void downloadBegan(ArchiveDownloadRequest<IBeatmapSetInfo> request) => Schedule(() => private void downloadBegan(ArchiveDownloadRequest<IBeatmapSetInfo> request) => Schedule(() =>

View File

@ -78,13 +78,13 @@ namespace osu.Game.Online.Rooms
// handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow). // handles changes to hash that didn't occur from the import process (ie. a user editing the beatmap in the editor, somehow).
realmSubscription?.Dispose(); realmSubscription?.Dispose();
realmSubscription = filteredBeatmaps().QueryAsyncWithNotifications((items, changes, ___) => realmSubscription = realmContextFactory.Register(realm => filteredBeatmaps().QueryAsyncWithNotifications((items, changes, ___) =>
{ {
if (changes == null) if (changes == null)
return; return;
Scheduler.AddOnce(updateAvailability); Scheduler.AddOnce(updateAvailability);
}); }));
}, true); }, true);
} }

View File

@ -47,7 +47,7 @@ namespace osu.Game.Online
Downloader.DownloadBegan += downloadBegan; Downloader.DownloadBegan += downloadBegan;
Downloader.DownloadFailed += downloadFailed; Downloader.DownloadFailed += downloadFailed;
realmSubscription = realmContextFactory.Context.All<ScoreInfo>().Where(s => ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) || s.Hash == TrackedItem.Hash) && !s.DeletePending).QueryAsyncWithNotifications((items, changes, ___) => realmSubscription = realmContextFactory.Register(realm => realm.All<ScoreInfo>().Where(s => ((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID) || s.Hash == TrackedItem.Hash) && !s.DeletePending).QueryAsyncWithNotifications((items, changes, ___) =>
{ {
if (items.Any()) if (items.Any())
Schedule(() => UpdateState(DownloadState.LocallyAvailable)); Schedule(() => UpdateState(DownloadState.LocallyAvailable));
@ -59,7 +59,7 @@ namespace osu.Game.Online
attachDownload(Downloader.GetExistingDownload(scoreInfo)); attachDownload(Downloader.GetExistingDownload(scoreInfo));
}); });
} }
}); }));
} }
private void downloadBegan(ArchiveDownloadRequest<IScoreInfo> request) => Schedule(() => private void downloadBegan(ArchiveDownloadRequest<IScoreInfo> request) => Schedule(() =>

View File

@ -80,26 +80,31 @@ namespace osu.Game.Overlays
mods.BindValueChanged(_ => ResetTrackAdjustments(), true); mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
} }
private IQueryable<BeatmapSetInfo> availableBeatmaps => realmFactory.Context
.All<BeatmapSetInfo>()
.Where(s => !s.DeletePending);
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
var availableBeatmaps = realmFactory.Context
.All<BeatmapSetInfo>()
.Where(s => !s.DeletePending);
// ensure we're ready before completing async load. // ensure we're ready before completing async load.
// probably not a good way of handling this (as there is a period we aren't watching for changes until the realm subscription finishes up. // probably not a good way of handling this (as there is a period we aren't watching for changes until the realm subscription finishes up.
foreach (var s in availableBeatmaps) foreach (var s in availableBeatmaps)
beatmapSets.Add(s); beatmapSets.Add(s.Detach());
beatmapSubscription = availableBeatmaps.QueryAsyncWithNotifications(beatmapsChanged); beatmapSubscription = realmFactory.Register(realm => availableBeatmaps.QueryAsyncWithNotifications(beatmapsChanged));
} }
private void beatmapsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet changes, Exception error) private void beatmapsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet changes, Exception error)
{ {
if (changes == null) if (changes == null)
{
beatmapSets.Clear();
foreach (var s in sender)
beatmapSets.Add(s.Detach());
return; return;
}
foreach (int i in changes.InsertedIndices) foreach (int i in changes.InsertedIndices)
beatmapSets.Insert(i, sender[i].Detach()); beatmapSets.Insert(i, sender[i].Detach());

View File

@ -33,6 +33,10 @@ namespace osu.Game.Overlays.Settings.Sections.DebugSettings
using (realmFactory.BlockAllOperations()) using (realmFactory.BlockAllOperations())
{ {
} }
// retrieve context to revive realm subscriptions.
// TODO: should we do this from OsuGame or RealmContextFactory or something? Answer: yes.
var _ = realmFactory.Context;
} }
}, },
}; };

View File

@ -50,7 +50,12 @@ namespace osu.Game.Overlays.Settings.Sections
private RealmContextFactory realmFactory { get; set; } private RealmContextFactory realmFactory { get; set; }
private IDisposable realmSubscription; private IDisposable realmSubscription;
private IQueryable<SkinInfo> realmSkins;
private IQueryable<SkinInfo> realmSkins =>
realmFactory.Context.All<SkinInfo>()
.Where(s => !s.DeletePending)
.OrderByDescending(s => s.Protected) // protected skins should be at the top.
.ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase);
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor) private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor)
@ -78,20 +83,13 @@ namespace osu.Game.Overlays.Settings.Sections
skinDropdown.Current = dropdownBindable; skinDropdown.Current = dropdownBindable;
realmSkins = realmFactory.Context.All<SkinInfo>() realmSubscription = realmFactory.Register(realm => realmSkins
.Where(s => !s.DeletePending)
.OrderByDescending(s => s.Protected) // protected skins should be at the top.
.ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase);
realmSubscription = realmSkins
.QueryAsyncWithNotifications((sender, changes, error) => .QueryAsyncWithNotifications((sender, changes, error) =>
{ {
if (changes == null) // The first fire of this is a bit redundant due to the call below,
return; // but this is safest in case the subscription is restored after a context recycle.
// Eventually this should be handling the individual changes rather than refreshing the whole dropdown.
updateItems(); updateItems();
}); }));
updateItems(); updateItems();

View File

@ -190,13 +190,13 @@ namespace osu.Game.Screens.Select
{ {
base.LoadComplete(); base.LoadComplete();
subscriptionSets = getBeatmapSets(realmFactory.Context).QueryAsyncWithNotifications(beatmapSetsChanged); subscriptionSets = realmFactory.Register(realm => getBeatmapSets(realm).QueryAsyncWithNotifications(beatmapSetsChanged));
subscriptionBeatmaps = realmFactory.Context.All<BeatmapInfo>().Where(b => !b.Hidden).QueryAsyncWithNotifications(beatmapsChanged); subscriptionBeatmaps = realmFactory.Register(realm => realm.All<BeatmapInfo>().Where(b => !b.Hidden).QueryAsyncWithNotifications(beatmapsChanged));
// Can't use main subscriptions because we can't lookup deleted indices. // Can't use main subscriptions because we can't lookup deleted indices.
// https://github.com/realm/realm-dotnet/discussions/2634#discussioncomment-1605595. // https://github.com/realm/realm-dotnet/discussions/2634#discussioncomment-1605595.
subscriptionDeletedSets = realmFactory.Context.All<BeatmapSetInfo>().Where(s => s.DeletePending && !s.Protected).QueryAsyncWithNotifications(deletedBeatmapSetsChanged); subscriptionDeletedSets = realmFactory.Register(realm => realm.All<BeatmapSetInfo>().Where(s => s.DeletePending && !s.Protected).QueryAsyncWithNotifications(deletedBeatmapSetsChanged));
subscriptionHiddenBeatmaps = realmFactory.Context.All<BeatmapInfo>().Where(b => b.Hidden).QueryAsyncWithNotifications(beatmapsChanged); subscriptionHiddenBeatmaps = realmFactory.Register(realm => realm.All<BeatmapInfo>().Where(b => b.Hidden).QueryAsyncWithNotifications(beatmapsChanged));
} }
private void deletedBeatmapSetsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet changes, Exception error) private void deletedBeatmapSetsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet changes, Exception error)
@ -552,10 +552,11 @@ namespace osu.Game.Screens.Select
private void signalBeatmapsLoaded() private void signalBeatmapsLoaded()
{ {
Debug.Assert(BeatmapSetsLoaded == false); if (!BeatmapSetsLoaded)
{
BeatmapSetsChanged?.Invoke(); BeatmapSetsChanged?.Invoke();
BeatmapSetsLoaded = true; BeatmapSetsLoaded = true;
}
itemsCache.Invalidate(); itemsCache.Invalidate();
} }

View File

@ -48,18 +48,19 @@ namespace osu.Game.Screens.Select.Carousel
ruleset.BindValueChanged(_ => ruleset.BindValueChanged(_ =>
{ {
scoreSubscription?.Dispose(); scoreSubscription?.Dispose();
scoreSubscription = realmFactory.Context.All<ScoreInfo>() scoreSubscription = realmFactory.Register(realm =>
.Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0" realm.All<ScoreInfo>()
+ $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1" .Filter($"{nameof(ScoreInfo.User)}.{nameof(RealmUser.OnlineID)} == $0"
+ $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2" + $" && {nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} == $1"
+ $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName) + $" && {nameof(ScoreInfo.Ruleset)}.{nameof(RulesetInfo.ShortName)} == $2"
.OrderByDescending(s => s.TotalScore) + $" && {nameof(ScoreInfo.DeletePending)} == false", api.LocalUser.Value.Id, beatmapInfo.ID, ruleset.Value.ShortName)
.QueryAsyncWithNotifications((items, changes, ___) => .OrderByDescending(s => s.TotalScore)
{ .QueryAsyncWithNotifications((items, changes, ___) =>
Rank = items.FirstOrDefault()?.Rank; {
// Required since presence is changed via IsPresent override Rank = items.FirstOrDefault()?.Rank;
Invalidate(Invalidation.Presence); // Required since presence is changed via IsPresent override
}); Invalidate(Invalidation.Presence);
}));
}, true); }, true);
} }

View File

@ -44,9 +44,13 @@ namespace osu.Game.Screens.Select.Leaderboards
beatmapInfo = value; beatmapInfo = value;
Scores = null; Scores = null;
UpdateScores(); if (IsOnlineScope)
if (IsLoaded) UpdateScores();
refreshRealmSubscription(); else
{
if (IsLoaded)
refreshRealmSubscription();
}
} }
} }
@ -109,15 +113,14 @@ namespace osu.Game.Screens.Select.Leaderboards
if (beatmapInfo == null) if (beatmapInfo == null)
return; return;
scoreSubscription = realmFactory.Context.All<ScoreInfo>() scoreSubscription = realmFactory.Register(realm =>
.Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} = $0", beatmapInfo.ID) realm.All<ScoreInfo>()
.QueryAsyncWithNotifications((_, changes, ___) => .Filter($"{nameof(ScoreInfo.BeatmapInfo)}.{nameof(BeatmapInfo.ID)} = $0", beatmapInfo.ID)
{ .QueryAsyncWithNotifications((_, changes, ___) =>
if (changes == null) {
return; if (!IsOnlineScope)
RefreshScores();
RefreshScores(); }));
});
} }
protected override void Reset() protected override void Reset()

View File

@ -79,17 +79,17 @@ namespace osu.Game.Screens.Spectate
playingUserStates.BindTo(spectatorClient.PlayingUserStates); playingUserStates.BindTo(spectatorClient.PlayingUserStates);
playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true); playingUserStates.BindCollectionChanged(onPlayingUserStatesChanged, true);
realmSubscription = realmContextFactory.Context realmSubscription = realmContextFactory.Register(realm =>
.All<BeatmapSetInfo>() realm.All<BeatmapSetInfo>()
.Where(s => !s.DeletePending) .Where(s => !s.DeletePending)
.QueryAsyncWithNotifications((items, changes, ___) => .QueryAsyncWithNotifications((items, changes, ___) =>
{ {
if (changes?.InsertedIndices == null) if (changes?.InsertedIndices == null)
return; return;
foreach (int c in changes.InsertedIndices) foreach (int c in changes.InsertedIndices)
beatmapUpdated(items[c]); beatmapUpdated(items[c]);
}); }));
foreach ((int id, var _) in userMap) foreach ((int id, var _) in userMap)
spectatorClient.WatchUser(id); spectatorClient.WatchUser(id);