1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-14 15:17:27 +08:00

Merge pull request #28420 from bdach/mutually-exclusive-save-related-actions

Disallow running save-related operations concurrently
This commit is contained in:
Dean Herbert 2024-06-11 01:03:47 +09:00 committed by GitHub
commit cd3b455341
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 98 additions and 31 deletions

View File

@ -4,6 +4,7 @@
#nullable disable #nullable disable
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -22,6 +23,8 @@ namespace osu.Game.Screens.Edit
{ {
public TestGameplayButton TestGameplayButton { get; private set; } public TestGameplayButton TestGameplayButton { get; private set; }
private IBindable<bool> saveInProgress;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider, Editor editor) private void load(OverlayColourProvider colourProvider, Editor editor)
{ {
@ -74,6 +77,15 @@ namespace osu.Game.Screens.Edit
} }
} }
}; };
saveInProgress = editor.MutationTracker.InProgress.GetBoundCopy();
}
protected override void LoadComplete()
{
base.LoadComplete();
saveInProgress.BindValueChanged(_ => TestGameplayButton.Enabled.Value = !saveInProgress.Value, true);
} }
} }
} }

View File

@ -6,6 +6,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework; using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -35,6 +36,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.OSD; using osu.Game.Overlays.OSD;
@ -49,6 +51,7 @@ using osu.Game.Screens.Edit.GameplayTest;
using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Timing;
using osu.Game.Screens.Edit.Verify; using osu.Game.Screens.Edit.Verify;
using osu.Game.Screens.OnlinePlay;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Users; using osu.Game.Users;
using osuTK.Input; using osuTK.Input;
@ -142,6 +145,13 @@ namespace osu.Game.Screens.Edit
private readonly Bindable<bool> samplePlaybackDisabled = new Bindable<bool>(); private readonly Bindable<bool> samplePlaybackDisabled = new Bindable<bool>();
private bool canSave; private bool canSave;
private readonly List<MenuItem> saveRelatedMenuItems = new List<MenuItem>();
/// <summary>
/// Tracks ongoing mutually-exclusive operations related to changing the beatmap
/// (e.g. save, export).
/// </summary>
public OngoingOperationTracker MutationTracker { get; } = new OngoingOperationTracker();
protected bool ExitConfirmed { get; private set; } protected bool ExitConfirmed { get; private set; }
@ -328,7 +338,7 @@ namespace osu.Game.Screens.Edit
{ {
new MenuItem(CommonStrings.MenuBarFile) new MenuItem(CommonStrings.MenuBarFile)
{ {
Items = createFileMenuItems() Items = createFileMenuItems().ToList()
}, },
new MenuItem(CommonStrings.MenuBarEdit) new MenuItem(CommonStrings.MenuBarEdit)
{ {
@ -382,6 +392,7 @@ namespace osu.Game.Screens.Edit
}, },
}, },
bottomBar = new BottomBar(), bottomBar = new BottomBar(),
MutationTracker,
} }
}); });
changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler?.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true);
@ -402,6 +413,12 @@ namespace osu.Game.Screens.Edit
Mode.BindValueChanged(onModeChanged, true); Mode.BindValueChanged(onModeChanged, true);
musicController.TrackChanged += onTrackChanged; musicController.TrackChanged += onTrackChanged;
MutationTracker.InProgress.BindValueChanged(_ =>
{
foreach (var item in saveRelatedMenuItems)
item.Action.Disabled = MutationTracker.InProgress.Value;
}, true);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
@ -440,12 +457,13 @@ namespace osu.Game.Screens.Edit
{ {
if (HasUnsavedChanges) if (HasUnsavedChanges)
{ {
dialogOverlay.Push(new SaveRequiredPopupDialog("The beatmap will be saved in order to test it.", () => dialogOverlay.Push(new SaveRequiredPopupDialog("The beatmap will be saved in order to test it.", () => attemptMutationOperation(() =>
{ {
if (!Save()) return; if (!Save()) return false;
pushEditorPlayer(); pushEditorPlayer();
})); return true;
})));
} }
else else
{ {
@ -455,6 +473,26 @@ namespace osu.Game.Screens.Edit
void pushEditorPlayer() => this.Push(new EditorPlayerLoader(this)); void pushEditorPlayer() => this.Push(new EditorPlayerLoader(this));
} }
private bool attemptMutationOperation(Func<bool> mutationOperation)
{
if (MutationTracker.InProgress.Value)
return false;
using (MutationTracker.BeginOperation())
return mutationOperation.Invoke();
}
private bool attemptAsyncMutationOperation(Func<Task> mutationTask)
{
if (MutationTracker.InProgress.Value)
return false;
var operation = MutationTracker.BeginOperation();
var task = mutationTask.Invoke();
task.FireAndForget(operation.Dispose, _ => operation.Dispose());
return true;
}
/// <summary> /// <summary>
/// Saves the currently edited beatmap. /// Saves the currently edited beatmap.
/// </summary> /// </summary>
@ -520,8 +558,7 @@ namespace osu.Game.Screens.Edit
if (e.Repeat) if (e.Repeat)
return false; return false;
Save(); return attemptMutationOperation(Save);
return true;
} }
return false; return false;
@ -787,7 +824,8 @@ namespace osu.Game.Screens.Edit
private void confirmExitWithSave() private void confirmExitWithSave()
{ {
if (!Save()) return; if (!attemptMutationOperation(Save))
return;
ExitConfirmed = true; ExitConfirmed = true;
this.Exit(); this.Exit();
@ -1020,25 +1058,35 @@ namespace osu.Game.Screens.Edit
lastSavedHash = changeHandler?.CurrentStateHash; lastSavedHash = changeHandler?.CurrentStateHash;
} }
private List<MenuItem> createFileMenuItems() => new List<MenuItem> private IEnumerable<MenuItem> createFileMenuItems()
{ {
createDifficultyCreationMenu(), yield return createDifficultyCreationMenu();
createDifficultySwitchMenu(), yield return createDifficultySwitchMenu();
new OsuMenuItemSpacer(), yield return new OsuMenuItemSpacer();
new EditorMenuItem(EditorStrings.DeleteDifficulty, MenuItemType.Standard, deleteDifficulty) { Action = { Disabled = Beatmap.Value.BeatmapSetInfo.Beatmaps.Count < 2 } }, yield return new EditorMenuItem(EditorStrings.DeleteDifficulty, MenuItemType.Standard, deleteDifficulty) { Action = { Disabled = Beatmap.Value.BeatmapSetInfo.Beatmaps.Count < 2 } };
new OsuMenuItemSpacer(), yield return new OsuMenuItemSpacer();
new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
createExportMenu(), var save = new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => attemptMutationOperation(Save));
new OsuMenuItemSpacer(), saveRelatedMenuItems.Add(save);
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit) yield return save;
};
if (RuntimeInfo.IsDesktop)
{
var export = createExportMenu();
saveRelatedMenuItems.AddRange(export.Items);
yield return export;
}
yield return new OsuMenuItemSpacer();
yield return new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit);
}
private EditorMenuItem createExportMenu() private EditorMenuItem createExportMenu()
{ {
var exportItems = new List<MenuItem> var exportItems = new List<MenuItem>
{ {
new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, () => exportBeatmap(false)) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, () => exportBeatmap(false)),
new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, () => exportBeatmap(true)) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, () => exportBeatmap(true)),
}; };
return new EditorMenuItem(CommonStrings.Export) { Items = exportItems }; return new EditorMenuItem(CommonStrings.Export) { Items = exportItems };
@ -1048,24 +1096,25 @@ namespace osu.Game.Screens.Edit
{ {
if (HasUnsavedChanges) if (HasUnsavedChanges)
{ {
dialogOverlay.Push(new SaveRequiredPopupDialog("The beatmap will be saved in order to export it.", () => dialogOverlay.Push(new SaveRequiredPopupDialog("The beatmap will be saved in order to export it.", () => attemptAsyncMutationOperation(() =>
{ {
if (!Save()) return; if (!Save())
return Task.CompletedTask;
runExport(); return runExport();
})); })));
} }
else else
{ {
runExport(); attemptAsyncMutationOperation(runExport);
} }
void runExport() Task runExport()
{ {
if (legacy) if (legacy)
beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo); return beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo);
else else
beatmapManager.Export(Beatmap.Value.BeatmapSetInfo); return beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
} }
} }
@ -1116,6 +1165,8 @@ namespace osu.Game.Screens.Edit
foreach (var ruleset in rulesets.AvailableRulesets) foreach (var ruleset in rulesets.AvailableRulesets)
rulesetItems.Add(new EditorMenuItem(ruleset.Name, MenuItemType.Standard, () => CreateNewDifficulty(ruleset))); rulesetItems.Add(new EditorMenuItem(ruleset.Name, MenuItemType.Standard, () => CreateNewDifficulty(ruleset)));
saveRelatedMenuItems.AddRange(rulesetItems);
return new EditorMenuItem(EditorStrings.CreateNewDifficulty) { Items = rulesetItems }; return new EditorMenuItem(EditorStrings.CreateNewDifficulty) { Items = rulesetItems };
} }
@ -1125,10 +1176,14 @@ namespace osu.Game.Screens.Edit
{ {
dialogOverlay.Push(new SaveRequiredPopupDialog("This beatmap will be saved in order to create another difficulty.", () => dialogOverlay.Push(new SaveRequiredPopupDialog("This beatmap will be saved in order to create another difficulty.", () =>
{ {
if (!Save()) attemptMutationOperation(() =>
return; {
if (!Save())
return false;
CreateNewDifficulty(rulesetInfo); CreateNewDifficulty(rulesetInfo);
return true;
});
})); }));
return; return;