Merge branch 'master' into fix-out-of-order-events-on-block-fail

This commit is contained in:
Dan Balasescu
2022-01-26 19:08:55 +09:00
committed by GitHub
14 changed files with 278 additions and 218 deletions

View File

@ -111,7 +111,7 @@ namespace osu.Game.Beatmaps
public int GridSize { get; set; }
public double TimelineZoom { get; set; }
public double TimelineZoom { get; set; } = 1.0;
[Ignored]
public CountdownType Countdown { get; set; } = CountdownType.Normal;

View File

@ -286,6 +286,7 @@ namespace osu.Game.Database
var transaction = r.BeginWrite();
int written = 0;
int missing = 0;
try
{
@ -300,6 +301,13 @@ namespace osu.Game.Database
var beatmap = r.All<BeatmapInfo>().First(b => b.Hash == score.BeatmapInfo.Hash);
var ruleset = r.Find<RulesetInfo>(score.Ruleset.ShortName);
if (ruleset == null)
{
log($"Skipping {++missing} scores with missing ruleset");
continue;
}
var user = new RealmUser
{
OnlineID = score.User.OnlineID,

View File

@ -216,12 +216,6 @@ namespace osu.Game.Database
return new RealmLiveUnmanaged<T>(realmObject);
}
public static List<Live<T>> ToLive<T>(this IEnumerable<T> realmList, RealmAccess realm)
where T : RealmObject, IHasGuidPrimaryKey
{
return realmList.Select(l => new RealmLive<T>(l, realm)).Cast<Live<T>>().ToList();
}
public static Live<T> ToLive<T>(this T realmObject, RealmAccess realm)
where T : RealmObject, IHasGuidPrimaryKey
{

View File

@ -11,7 +11,7 @@ namespace osu.Game.Models
{
public int OnlineID { get; set; } = 1;
public string Username { get; set; }
public string Username { get; set; } = string.Empty;
public bool IsBot => false;

View File

@ -18,6 +18,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation;
using osu.Game.Skinning;
using osu.Game.Skinning.Editor;
using Realms;
namespace osu.Game.Overlays.Settings.Sections
{
@ -41,7 +42,7 @@ namespace osu.Game.Overlays.Settings.Sections
Name = "<Random Skin>",
}.ToLiveUnmanaged();
private List<Live<SkinInfo>> skinItems;
private readonly List<Live<SkinInfo>> dropdownItems = new List<Live<SkinInfo>>();
[Resolved]
private SkinManager skins { get; set; }
@ -51,12 +52,6 @@ namespace osu.Game.Overlays.Settings.Sections
private IDisposable realmSubscription;
private IQueryable<SkinInfo> queryRealmSkins() =>
realm.Realm.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)]
private void load(OsuConfigManager config, [CanBeNull] SkinEditorOverlay skinEditor)
{
@ -83,37 +78,58 @@ namespace osu.Game.Overlays.Settings.Sections
skinDropdown.Current = dropdownBindable;
realmSubscription = realm.RegisterForNotifications(r => queryRealmSkins(), (sender, changes, error) =>
{
// The first fire of this is a bit redundant due to the call below,
// but this is safest in case the subscription is restored after a context recycle.
updateItems();
});
updateItems();
realmSubscription = realm.RegisterForNotifications(r => realm.Realm.All<SkinInfo>()
.Where(s => !s.DeletePending)
.OrderByDescending(s => s.Protected) // protected skins should be at the top.
.ThenBy(s => s.Name, StringComparer.OrdinalIgnoreCase), skinsChanged);
configBindable.BindValueChanged(id => Scheduler.AddOnce(updateSelectedSkinFromConfig));
updateSelectedSkinFromConfig();
dropdownBindable.BindValueChanged(skin =>
dropdownBindable.BindValueChanged(dropdownSelectionChanged);
}
private void dropdownSelectionChanged(ValueChangedEvent<Live<SkinInfo>> skin)
{
// Only handle cases where it's clear the user has intent to change skins.
if (skin.OldValue == null) return;
if (skin.NewValue.Equals(random_skin_info))
{
if (skin.NewValue.Equals(random_skin_info))
var skinBefore = skins.CurrentSkinInfo.Value;
skins.SelectRandomSkin();
if (skinBefore == skins.CurrentSkinInfo.Value)
{
var skinBefore = skins.CurrentSkinInfo.Value;
skins.SelectRandomSkin();
if (skinBefore == skins.CurrentSkinInfo.Value)
{
// the random selection didn't change the skin, so we should manually update the dropdown to match.
dropdownBindable.Value = skins.CurrentSkinInfo.Value;
}
return;
// the random selection didn't change the skin, so we should manually update the dropdown to match.
dropdownBindable.Value = skins.CurrentSkinInfo.Value;
}
configBindable.Value = skin.NewValue.ID.ToString();
});
return;
}
configBindable.Value = skin.NewValue.ID.ToString();
}
private void skinsChanged(IRealmCollection<SkinInfo> sender, ChangeSet changes, Exception error)
{
// This can only mean that realm is recycling, else we would see the protected skins.
// Because we are using `Live<>` in this class, we don't need to worry about this scenario too much.
if (!sender.Any())
return;
int protectedCount = sender.Count(s => s.Protected);
// For simplicity repopulate the full list.
// In the future we should change this to properly handle ChangeSet events.
dropdownItems.Clear();
foreach (var skin in sender)
dropdownItems.Add(skin.ToLive(realm));
dropdownItems.Insert(protectedCount, random_skin_info);
skinDropdown.Items = dropdownItems;
updateSelectedSkinFromConfig();
}
private void updateSelectedSkinFromConfig()
@ -126,17 +142,6 @@ namespace osu.Game.Overlays.Settings.Sections
dropdownBindable.Value = skin ?? skinDropdown.Items.First();
}
private void updateItems()
{
int protectedCount = queryRealmSkins().Count(s => s.Protected);
skinItems = queryRealmSkins().ToLive(realm);
skinItems.Insert(protectedCount, random_skin_info);
skinDropdown.Items = skinItems;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

View File

@ -28,21 +28,15 @@ namespace osu.Game.Rulesets
public Ruleset CreateInstance()
{
if (!Available)
throw new RulesetLoadException(@"Ruleset not available");
return null;
var type = Type.GetType(InstantiationInfo);
if (type == null)
throw new RulesetLoadException(@"Type lookup failure");
return null;
var ruleset = Activator.CreateInstance(type) as Ruleset;
if (ruleset == null)
throw new RulesetLoadException(@"Instantiation failure");
// overwrite the pre-populated RulesetInfo with a potentially database attached copy.
// ruleset.RulesetInfo = this;
return ruleset;
}

View File

@ -133,6 +133,11 @@ namespace osu.Game.Scoring
var clone = (ScoreInfo)this.Detach().MemberwiseClone();
clone.Statistics = new Dictionary<HitResult, int>(clone.Statistics);
clone.RealmUser = new RealmUser
{
OnlineID = RealmUser.OnlineID,
Username = RealmUser.Username,
};
return clone;
}

View File

@ -0,0 +1,67 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Select;
using osuTK.Input;
namespace osu.Game.Tests.Visual
{
/// <summary>
/// Tests the general expected flow of creating a new beatmap, saving it, then loading it back from song select.
/// </summary>
public class EditorSavingTestScene : OsuGameTestScene
{
protected Editor Editor => Game.ChildrenOfType<Editor>().FirstOrDefault();
protected EditorBeatmap EditorBeatmap => (EditorBeatmap)Editor.Dependencies.Get(typeof(EditorBeatmap));
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("set default beatmap", () => Game.Beatmap.SetDefault());
PushAndConfirm(() => new EditorLoader());
AddUntilStep("wait for editor load", () => Editor?.IsLoaded == true);
AddUntilStep("wait for metadata screen load", () => Editor.ChildrenOfType<MetadataSection>().FirstOrDefault()?.IsLoaded == true);
// We intentionally switch away from the metadata screen, else there is a feedback loop with the textbox handling which causes metadata changes below to get overwritten.
AddStep("Enter compose mode", () => InputManager.Key(Key.F1));
AddUntilStep("Wait for compose mode load", () => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true);
}
protected void SaveEditor()
{
AddStep("Save", () => InputManager.Keys(PlatformAction.Save));
}
protected void ReloadEditorToSameBeatmap()
{
AddStep("Exit", () => InputManager.Key(Key.Escape));
AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
SongSelect songSelect = null;
PushAndConfirm(() => songSelect = new PlaySongSelect());
AddUntilStep("wait for carousel load", () => songSelect.BeatmapSetsLoaded);
AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault);
AddStep("Open options", () => InputManager.Key(Key.F3));
AddStep("Enter editor", () => InputManager.Key(Key.Number5));
AddUntilStep("Wait for editor load", () => Editor != null);
}
}
}