1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 18:33:20 +08:00

Merge branch 'master' into multi-spectator-fix-startup-delay

This commit is contained in:
Bartłomiej Dach 2023-08-16 10:34:20 +02:00 committed by GitHub
commit 35af15f491
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 82 additions and 66 deletions

View File

@ -92,25 +92,6 @@ namespace osu.Game.Tests.Visual.Editing
} }
[Test] [Test]
[FlakyTest]
/*
* Fail rate around 1.2%.
*
* Failing with realm refetch occasionally being null.
* My only guess is that the WorkingBeatmap at SetupScreen is dummy instead of the true one.
* If it's something else, we have larger issues with realm, but I don't think that's the case.
*
* at osu.Framework.Logging.ThrowingTraceListener.Fail(String message1, String message2)
* at System.Diagnostics.TraceInternal.Fail(String message, String detailMessage)
* at System.Diagnostics.TraceInternal.TraceProvider.Fail(String message, String detailMessage)
* at System.Diagnostics.Debug.Fail(String message, String detailMessage)
* at osu.Game.Database.ModelManager`1.<>c__DisplayClass8_0.<performFileOperation>b__0(Realm realm) ModelManager.cs:line 50
* at osu.Game.Database.RealmExtensions.Write(Realm realm, Action`1 function) RealmExtensions.cs:line 14
* at osu.Game.Database.ModelManager`1.performFileOperation(TModel item, Action`1 operation) ModelManager.cs:line 47
* at osu.Game.Database.ModelManager`1.AddFile(TModel item, Stream contents, String filename) ModelManager.cs:line 37
* at osu.Game.Screens.Edit.Setup.ResourcesSection.ChangeAudioTrack(FileInfo source) ResourcesSection.cs:line 115
* at osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation.<TestAddAudioTrack>b__11_0() TestSceneEditorBeatmapCreation.cs:line 101
*/
public void TestAddAudioTrack() public void TestAddAudioTrack()
{ {
AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual); AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual);

View File

@ -84,12 +84,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
} }
[Test] [Test]
public void TestFocusOnTabKeyWhenExpanded() public void TestFocusOnEnterKeyWhenExpanded()
{ {
setLocalUserPlaying(true); setLocalUserPlaying(true);
assertChatFocused(false); assertChatFocused(false);
AddStep("press tab", () => InputManager.Key(Key.Tab)); AddStep("press enter", () => InputManager.Key(Key.Enter));
assertChatFocused(true); assertChatFocused(true);
} }
@ -99,19 +99,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
setLocalUserPlaying(true); setLocalUserPlaying(true);
assertChatFocused(false); assertChatFocused(false);
AddStep("press tab", () => InputManager.Key(Key.Tab)); AddStep("press enter", () => InputManager.Key(Key.Enter));
assertChatFocused(true); assertChatFocused(true);
AddStep("press escape", () => InputManager.Key(Key.Escape)); AddStep("press escape", () => InputManager.Key(Key.Escape));
assertChatFocused(false); assertChatFocused(false);
} }
[Test] [Test]
public void TestFocusOnTabKeyWhenNotExpanded() public void TestFocusOnEnterKeyWhenNotExpanded()
{ {
AddStep("set not expanded", () => chatDisplay.Expanded.Value = false); AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
AddUntilStep("is not visible", () => !chatDisplay.IsPresent); AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
AddStep("press tab", () => InputManager.Key(Key.Tab)); AddStep("press enter", () => InputManager.Key(Key.Enter));
assertChatFocused(true); assertChatFocused(true);
AddUntilStep("is visible", () => chatDisplay.IsPresent); AddUntilStep("is visible", () => chatDisplay.IsPresent);
@ -120,21 +120,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("is not visible", () => !chatDisplay.IsPresent); AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
} }
[Test]
public void TestFocusToggleViaAction()
{
AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
AddStep("press tab", () => InputManager.Key(Key.Tab));
assertChatFocused(true);
AddUntilStep("is visible", () => chatDisplay.IsPresent);
AddStep("press tab", () => InputManager.Key(Key.Tab));
assertChatFocused(false);
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
}
private void assertChatFocused(bool isFocused) => private void assertChatFocused(bool isFocused) =>
AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused); AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused);

View File

@ -237,6 +237,12 @@ namespace osu.Game.Configuration
value: disabledState ? CommonStrings.Disabled.ToLower() : CommonStrings.Enabled.ToLower(), value: disabledState ? CommonStrings.Disabled.ToLower() : CommonStrings.Enabled.ToLower(),
shortcut: LookupKeyBindings(GlobalAction.ToggleGameplayMouseButtons)) shortcut: LookupKeyBindings(GlobalAction.ToggleGameplayMouseButtons))
), ),
new TrackedSetting<bool>(OsuSetting.GameplayLeaderboard, state => new SettingDescription(
rawValue: state,
name: GlobalActionKeyBindingStrings.ToggleInGameLeaderboard,
value: state ? CommonStrings.Enabled.ToLower() : CommonStrings.Disabled.ToLower(),
shortcut: LookupKeyBindings(GlobalAction.ToggleInGameLeaderboard))
),
new TrackedSetting<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode, visibilityMode => new SettingDescription( new TrackedSetting<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode, visibilityMode => new SettingDescription(
rawValue: visibilityMode, rawValue: visibilityMode,
name: GameplaySettingsStrings.HUDVisibilityMode, name: GameplaySettingsStrings.HUDVisibilityMode,

View File

@ -52,7 +52,7 @@ namespace osu.Game.Database
// (ie. if an async import finished very recently). // (ie. if an async import finished very recently).
Realm.Realm.Write(realm => Realm.Realm.Write(realm =>
{ {
var managed = realm.Find<TModel>(item.ID); var managed = realm.FindWithRefresh<TModel>(item.ID);
Debug.Assert(managed != null); Debug.Assert(managed != null);
operation(managed); operation(managed);

View File

@ -82,8 +82,9 @@ namespace osu.Game.Database
/// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations. /// 30 2023-06-16 Run migration of old lazer scores again. This time with more correct rounding considerations.
/// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores. /// 31 2023-06-26 Add Version and LegacyTotalScore to ScoreInfo, set Version to 30000002 and copy TotalScore into LegacyTotalScore for legacy scores.
/// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files. /// 32 2023-07-09 Populate legacy scores with the ScoreV2 mod (and restore TotalScore to the legacy total for such scores) using replay files.
/// 33 2023-08-16 Reset default chat toggle key binding to avoid conflict with newly added leaderboard toggle key binding.
/// </summary> /// </summary>
private const int schema_version = 32; private const int schema_version = 33;
/// <summary> /// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods. /// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
@ -771,6 +772,7 @@ namespace osu.Game.Database
break; break;
case 8: case 8:
{
// Ctrl -/+ now adjusts UI scale so let's clear any bindings which overlap these combinations. // Ctrl -/+ now adjusts UI scale so let's clear any bindings which overlap these combinations.
// New defaults will be populated by the key store afterwards. // New defaults will be populated by the key store afterwards.
var keyBindings = migration.NewRealm.All<RealmKeyBinding>(); var keyBindings = migration.NewRealm.All<RealmKeyBinding>();
@ -784,6 +786,7 @@ namespace osu.Game.Database
migration.NewRealm.Remove(decreaseSpeedBinding); migration.NewRealm.Remove(decreaseSpeedBinding);
break; break;
}
case 9: case 9:
// Pretty pointless to do this as beatmaps aren't really loaded via realm yet, but oh well. // Pretty pointless to do this as beatmaps aren't really loaded via realm yet, but oh well.
@ -838,6 +841,7 @@ namespace osu.Game.Database
break; break;
case 11: case 11:
{
string keyBindingClassName = getMappedOrOriginalName(typeof(RealmKeyBinding)); string keyBindingClassName = getMappedOrOriginalName(typeof(RealmKeyBinding));
if (!migration.OldRealm.Schema.TryFindObjectSchema(keyBindingClassName, out _)) if (!migration.OldRealm.Schema.TryFindObjectSchema(keyBindingClassName, out _))
@ -864,6 +868,7 @@ namespace osu.Game.Database
} }
break; break;
}
case 14: case 14:
foreach (var beatmap in migration.NewRealm.All<BeatmapInfo>()) foreach (var beatmap in migration.NewRealm.All<BeatmapInfo>())
@ -1012,6 +1017,19 @@ namespace osu.Game.Database
break; break;
} }
case 33:
{
// Clear default bindings for the chat focus toggle,
// as they would conflict with the newly-added leaderboard toggle.
var keyBindings = migration.NewRealm.All<RealmKeyBinding>();
var toggleChatBind = keyBindings.FirstOrDefault(bind => bind.ActionInt == (int)GlobalAction.ToggleChatFocus);
if (toggleChatBind != null && toggleChatBind.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Tab }))
migration.NewRealm.Remove(toggleChatBind);
break;
}
} }
Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms"); Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms");

View File

@ -8,6 +8,34 @@ namespace osu.Game.Database
{ {
public static class RealmExtensions public static class RealmExtensions
{ {
/// <summary>
/// Performs a <see cref="Realm.Find{T}(System.Nullable{long})"/>.
/// If a match was not found, a <see cref="Realm.Refresh"/> is performed before trying a second time.
/// This ensures that an instance is found even if the realm requested against was not in a consistent state.
/// </summary>
/// <param name="realm">The realm to operate on.</param>
/// <param name="id">The ID of the entity to find in the realm.</param>
/// <typeparam name="T">The type of the entity to find in the realm.</typeparam>
/// <returns>
/// The retrieved entity of type <typeparamref name="T"/>.
/// Can be <see langword="null"/> if the entity is still not found by <paramref name="id"/> even after a refresh.
/// </returns>
public static T? FindWithRefresh<T>(this Realm realm, Guid id) where T : IRealmObject
{
var found = realm.Find<T>(id);
if (found == null)
{
// It may be that we access this from the update thread before a refresh has taken place.
// To ensure that behaviour matches what we'd expect (the object generally *should be* available), force
// a refresh to bring in any off-thread changes immediately.
realm.Refresh();
found = realm.Find<T>(id);
}
return found;
}
/// <summary> /// <summary>
/// Perform a write operation against the provided realm instance. /// Perform a write operation against the provided realm instance.
/// </summary> /// </summary>

View File

@ -30,7 +30,7 @@ namespace osu.Game.Database
/// <summary> /// <summary>
/// Construct a new instance of live realm data. /// Construct a new instance of live realm data.
/// </summary> /// </summary>
/// <param name="data">The realm data.</param> /// <param name="data">The realm data. Must be managed (see <see cref="IRealmObjectBase.IsManaged"/>).</param>
/// <param name="realm">The realm factory the data was sourced from. May be null for an unmanaged object.</param> /// <param name="realm">The realm factory the data was sourced from. May be null for an unmanaged object.</param>
public RealmLive(T data, RealmAccess realm) public RealmLive(T data, RealmAccess realm)
: base(data.ID) : base(data.ID)
@ -62,7 +62,7 @@ namespace osu.Game.Database
return; return;
} }
perform(retrieveFromID(r)); perform(r.FindWithRefresh<T>(ID)!);
RealmLiveStatistics.USAGE_ASYNC.Value++; RealmLiveStatistics.USAGE_ASYNC.Value++;
}); });
} }
@ -84,7 +84,7 @@ namespace osu.Game.Database
return realm.Run(r => return realm.Run(r =>
{ {
var returnData = perform(retrieveFromID(r)); var returnData = perform(r.FindWithRefresh<T>(ID)!);
RealmLiveStatistics.USAGE_ASYNC.Value++; RealmLiveStatistics.USAGE_ASYNC.Value++;
if (returnData is RealmObjectBase realmObject && realmObject.IsManaged) if (returnData is RealmObjectBase realmObject && realmObject.IsManaged)
@ -141,25 +141,10 @@ namespace osu.Game.Database
} }
dataIsFromUpdateThread = true; dataIsFromUpdateThread = true;
data = retrieveFromID(realm.Realm); data = realm.Realm.FindWithRefresh<T>(ID)!;
RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++; RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++;
} }
private T retrieveFromID(Realm realm)
{
var found = realm.Find<T>(ID);
if (found == null)
{
// It may be that we access this from the update thread before a refresh has taken place.
// To ensure that behaviour matches what we'd expect (the object *is* available), force
// a refresh to bring in any off-thread changes immediately.
realm.Refresh();
found = realm.Find<T>(ID)!;
}
return found;
}
} }
internal static class RealmLiveStatistics internal static class RealmLiveStatistics

View File

@ -116,9 +116,10 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.F3 }, GlobalAction.DecreaseScrollSpeed), new KeyBinding(new[] { InputKey.F3 }, GlobalAction.DecreaseScrollSpeed),
new KeyBinding(new[] { InputKey.F4 }, GlobalAction.IncreaseScrollSpeed), new KeyBinding(new[] { InputKey.F4 }, GlobalAction.IncreaseScrollSpeed),
new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface), new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface),
new KeyBinding(InputKey.Tab, GlobalAction.ToggleInGameLeaderboard),
new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay), new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay),
new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD), new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD),
new KeyBinding(InputKey.Tab, GlobalAction.ToggleChatFocus), new KeyBinding(InputKey.Enter, GlobalAction.ToggleChatFocus),
new KeyBinding(InputKey.F1, GlobalAction.SaveReplay), new KeyBinding(InputKey.F1, GlobalAction.SaveReplay),
new KeyBinding(InputKey.F2, GlobalAction.ExportReplay), new KeyBinding(InputKey.F2, GlobalAction.ExportReplay),
}; };
@ -204,7 +205,6 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleMute))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleMute))]
ToggleMute, ToggleMute,
// In-Game Keybindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SkipCutscene))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SkipCutscene))]
SkipCutscene, SkipCutscene,
@ -232,7 +232,6 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickExit))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickExit))]
QuickExit, QuickExit,
// Game-wide beatmap music controller keybindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicNext))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicNext))]
MusicNext, MusicNext,
@ -260,7 +259,6 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))]
PauseGameplay, PauseGameplay,
// Editor
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorSetupMode))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorSetupMode))]
EditorSetupMode, EditorSetupMode,
@ -285,7 +283,6 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))]
ToggleInGameInterface, ToggleInGameInterface,
// Song select keybindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))]
ToggleModSelection, ToggleModSelection,
@ -378,5 +375,8 @@ namespace osu.Game.Input.Bindings
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleReplaySettings))] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleReplaySettings))]
ToggleReplaySettings, ToggleReplaySettings,
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))]
ToggleInGameLeaderboard,
} }
} }

View File

@ -219,6 +219,11 @@ namespace osu.Game.Localisation
/// </summary> /// </summary>
public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface"); public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface");
/// <summary>
/// "Toggle in-game leaderboard"
/// </summary>
public static LocalisableString ToggleInGameLeaderboard => new TranslatableString(getKey(@"toggle_in_game_leaderboard"), @"Toggle in-game leaderboard");
/// <summary> /// <summary>
/// "Toggle mod select" /// "Toggle mod select"
/// </summary> /// </summary>

View File

@ -199,6 +199,8 @@ namespace osu.Game.Screens.Edit
if (loadableBeatmap is DummyWorkingBeatmap) if (loadableBeatmap is DummyWorkingBeatmap)
{ {
Logger.Log("Editor was loaded without a valid beatmap; creating a new beatmap.");
isNewBeatmap = true; isNewBeatmap = true;
loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value); loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value);

View File

@ -81,6 +81,7 @@ namespace osu.Game.Screens.Play
public Bindable<bool> ShowHud { get; } = new BindableBool(); public Bindable<bool> ShowHud { get; } = new BindableBool();
private Bindable<HUDVisibilityMode> configVisibilityMode; private Bindable<HUDVisibilityMode> configVisibilityMode;
private Bindable<bool> configLeaderboardVisibility;
private Bindable<bool> configSettingsOverlay; private Bindable<bool> configSettingsOverlay;
private readonly BindableBool replayLoaded = new BindableBool(); private readonly BindableBool replayLoaded = new BindableBool();
@ -186,6 +187,7 @@ namespace osu.Game.Screens.Play
ModDisplay.Current.Value = mods; ModDisplay.Current.Value = mods;
configVisibilityMode = config.GetBindable<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode); configVisibilityMode = config.GetBindable<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode);
configLeaderboardVisibility = config.GetBindable<bool>(OsuSetting.GameplayLeaderboard);
configSettingsOverlay = config.GetBindable<bool>(OsuSetting.ReplaySettingsOverlay); configSettingsOverlay = config.GetBindable<bool>(OsuSetting.ReplaySettingsOverlay);
if (configVisibilityMode.Value == HUDVisibilityMode.Never && !hasShownNotificationOnce) if (configVisibilityMode.Value == HUDVisibilityMode.Never && !hasShownNotificationOnce)
@ -398,6 +400,10 @@ namespace osu.Game.Screens.Play
} }
return true; return true;
case GlobalAction.ToggleInGameLeaderboard:
configLeaderboardVisibility.Value = !configLeaderboardVisibility.Value;
return true;
} }
return false; return false;