mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 03:33:22 +08:00
Merge branch 'master' into fix-zip-encoding
This commit is contained in:
commit
82d1ebbd20
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -321,6 +322,30 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddUntilStep("nested input disabled", () => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType<PassThroughInputManager>().All(manager => !manager.UseParentInput));
|
AddUntilStep("nested input disabled", () => ((Player)Game.ScreenStack.CurrentScreen).ChildrenOfType<PassThroughInputManager>().All(manager => !manager.UseParentInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSkinSavesOnChange()
|
||||||
|
{
|
||||||
|
advanceToSongSelect();
|
||||||
|
openSkinEditor();
|
||||||
|
|
||||||
|
Guid editedSkinId = Guid.Empty;
|
||||||
|
AddStep("save skin id", () => editedSkinId = Game.Dependencies.Get<SkinManager>().CurrentSkinInfo.Value.ID);
|
||||||
|
AddStep("add skinnable component", () =>
|
||||||
|
{
|
||||||
|
skinEditor.ChildrenOfType<SkinComponentToolbox.ToolboxComponentButton>().First().TriggerClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("change to triangles skin", () => Game.Dependencies.Get<SkinManager>().SetSkinFromConfiguration(SkinInfo.TRIANGLES_SKIN.ToString()));
|
||||||
|
AddUntilStep("components loaded", () => Game.ChildrenOfType<SkinComponentsContainer>().All(c => c.ComponentsLoaded));
|
||||||
|
// sort of implicitly relies on song select not being skinnable.
|
||||||
|
// TODO: revisit if the above ever changes
|
||||||
|
AddUntilStep("skin changed", () => !skinEditor.ChildrenOfType<SkinBlueprint>().Any());
|
||||||
|
|
||||||
|
AddStep("change back to modified skin", () => Game.Dependencies.Get<SkinManager>().SetSkinFromConfiguration(editedSkinId.ToString()));
|
||||||
|
AddUntilStep("components loaded", () => Game.ChildrenOfType<SkinComponentsContainer>().All(c => c.ComponentsLoaded));
|
||||||
|
AddUntilStep("changes saved", () => skinEditor.ChildrenOfType<SkinBlueprint>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
private void advanceToSongSelect()
|
private void advanceToSongSelect()
|
||||||
{
|
{
|
||||||
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
PushAndConfirm(() => songSelect = new TestPlaySongSelect());
|
||||||
|
@ -35,6 +35,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
using osu.Game.Scoring.Legacy;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using Realms;
|
using Realms;
|
||||||
using Realms.Exceptions;
|
using Realms.Exceptions;
|
||||||
@ -321,12 +322,32 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
Logger.Error(e, "Your local database is too new to work with this version of osu!. Please close osu! and install the latest release to recover your data.");
|
Logger.Error(e, "Your local database is too new to work with this version of osu!. Please close osu! and install the latest release to recover your data.");
|
||||||
|
|
||||||
// If a newer version database already exists, don't backup again. We can presume that the first backup is the one we care about.
|
// If a newer version database already exists, don't create another backup. We can presume that the first backup is the one we care about.
|
||||||
if (!storage.Exists(newerVersionFilename))
|
if (!storage.Exists(newerVersionFilename))
|
||||||
createBackup(newerVersionFilename);
|
createBackup(newerVersionFilename);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// This error can occur due to file handles still being open by a previous instance.
|
||||||
|
// If this is the case, rather than assuming the realm file is corrupt, block game startup.
|
||||||
|
if (e.Message.StartsWith("SetEndOfFile() failed", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
// This will throw if the realm file is not available for write access after 5 seconds.
|
||||||
|
FileUtils.AttemptOperation(() =>
|
||||||
|
{
|
||||||
|
if (storage.Exists(Filename))
|
||||||
|
{
|
||||||
|
using (var _ = storage.GetStream(Filename, FileAccess.ReadWrite))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
// If the above eventually succeeds, try and continue startup as per normal.
|
||||||
|
// This may throw again but let's allow it to, and block startup.
|
||||||
|
return getRealmInstance();
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Error(e, "Realm startup failed with unrecoverable error; starting with a fresh database. A backup of your database has been made.");
|
Logger.Error(e, "Realm startup failed with unrecoverable error; starting with a fresh database. A backup of your database has been made.");
|
||||||
createBackup($"{Filename.Replace(realm_extension, string.Empty)}_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}_corrupt{realm_extension}");
|
createBackup($"{Filename.Replace(realm_extension, string.Empty)}_{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}_corrupt{realm_extension}");
|
||||||
}
|
}
|
||||||
@ -1142,33 +1163,18 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
Logger.Log($"Creating full realm database backup at {backupFilename}", LoggingTarget.Database);
|
Logger.Log($"Creating full realm database backup at {backupFilename}", LoggingTarget.Database);
|
||||||
|
|
||||||
int attempts = 10;
|
FileUtils.AttemptOperation(() =>
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
try
|
using (var source = storage.GetStream(Filename, mode: FileMode.Open))
|
||||||
{
|
{
|
||||||
using (var source = storage.GetStream(Filename, mode: FileMode.Open))
|
// source may not exist.
|
||||||
{
|
if (source == null)
|
||||||
// source may not exist.
|
return;
|
||||||
if (source == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (var destination = storage.GetStream(backupFilename, FileAccess.Write, FileMode.CreateNew))
|
using (var destination = storage.GetStream(backupFilename, FileAccess.Write, FileMode.CreateNew))
|
||||||
source.CopyTo(destination);
|
source.CopyTo(destination);
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
catch (IOException)
|
}, 20);
|
||||||
{
|
|
||||||
if (attempts-- <= 0)
|
|
||||||
throw;
|
|
||||||
|
|
||||||
// file may be locked during use.
|
|
||||||
Thread.Sleep(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -255,8 +255,11 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
// schedule ensures this only happens when the skin editor is visible.
|
// schedule ensures this only happens when the skin editor is visible.
|
||||||
// also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types).
|
// also avoid some weird endless recursion / bindable feedback loop (something to do with tracking skins across three different bindable types).
|
||||||
// probably something which will be factored out in a future database refactor so not too concerning for now.
|
// probably something which will be factored out in a future database refactor so not too concerning for now.
|
||||||
currentSkin.BindValueChanged(_ =>
|
currentSkin.BindValueChanged(val =>
|
||||||
{
|
{
|
||||||
|
if (val.OldValue != null && hasBegunMutating)
|
||||||
|
save(val.OldValue);
|
||||||
|
|
||||||
hasBegunMutating = false;
|
hasBegunMutating = false;
|
||||||
Scheduler.AddOnce(skinChanged);
|
Scheduler.AddOnce(skinChanged);
|
||||||
}, true);
|
}, true);
|
||||||
@ -537,7 +540,9 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
|
|
||||||
protected void Redo() => changeHandler?.RestoreState(1);
|
protected void Redo() => changeHandler?.RestoreState(1);
|
||||||
|
|
||||||
public void Save(bool userTriggered = true)
|
public void Save(bool userTriggered = true) => save(currentSkin.Value);
|
||||||
|
|
||||||
|
private void save(Skin skin, bool userTriggered = true)
|
||||||
{
|
{
|
||||||
if (!hasBegunMutating)
|
if (!hasBegunMutating)
|
||||||
return;
|
return;
|
||||||
@ -551,11 +556,11 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var t in targetContainers)
|
foreach (var t in targetContainers)
|
||||||
currentSkin.Value.UpdateDrawableTarget(t);
|
skin.UpdateDrawableTarget(t);
|
||||||
|
|
||||||
// In the case the save was user triggered, always show the save message to make them feel confident.
|
// In the case the save was user triggered, always show the save message to make them feel confident.
|
||||||
if (skins.Save(skins.CurrentSkin.Value) || userTriggered)
|
if (skins.Save(skin) || userTriggered)
|
||||||
onScreenDisplay?.Display(new SkinEditorToast(ToastStrings.SkinSaved, currentSkin.Value.SkinInfo.ToString() ?? "Unknown"));
|
onScreenDisplay?.Display(new SkinEditorToast(ToastStrings.SkinSaved, skin.SkinInfo.ToString() ?? "Unknown"));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e) => true;
|
protected override bool OnHover(HoverEvent e) => true;
|
||||||
|
Loading…
Reference in New Issue
Block a user