mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 09:42:54 +08:00
Merge branch 'master' into taiko-explosion-pooling
This commit is contained in:
commit
f01ece8c8d
@ -27,8 +27,8 @@
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="5.0.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
</ItemGroup>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
2
osu.Game.Tests/Resources/skin-with-space.ini
Normal file
2
osu.Game.Tests/Resources/skin-with-space.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[General]
|
||||
Version: 2
|
@ -91,6 +91,15 @@ namespace osu.Game.Tests.Skins
|
||||
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStripWhitespace()
|
||||
{
|
||||
var decoder = new LegacySkinDecoder();
|
||||
using (var resStream = TestResources.OpenResource("skin-with-space.ini"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeLatestVersion()
|
||||
{
|
||||
|
@ -56,6 +56,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
beatmaps.Add(new BeatmapInfo
|
||||
{
|
||||
Ruleset = rulesets.GetRuleset(i % 4),
|
||||
RulesetID = i % 4, // workaround for efcore 5 compatibility.
|
||||
OnlineBeatmapID = beatmapId,
|
||||
Length = length,
|
||||
BPM = bpm,
|
||||
|
@ -186,6 +186,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
Metadata = metadata,
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
Ruleset = ruleset,
|
||||
RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility.
|
||||
StarDifficulty = difficultyIndex + 1,
|
||||
Version = $"SR{difficultyIndex + 1}"
|
||||
}).ToList()
|
||||
|
@ -911,9 +911,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
int length = RNG.Next(30000, 200000);
|
||||
double bpm = RNG.NextSingle(80, 200);
|
||||
|
||||
var ruleset = getRuleset();
|
||||
beatmaps.Add(new BeatmapInfo
|
||||
{
|
||||
Ruleset = getRuleset(),
|
||||
Ruleset = ruleset,
|
||||
RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility.
|
||||
OnlineBeatmapID = beatmapId,
|
||||
Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
|
||||
Length = length,
|
||||
|
@ -6,7 +6,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
|
@ -17,12 +17,12 @@ namespace osu.Game.Beatmaps
|
||||
public abstract class BeatmapConverter<T> : IBeatmapConverter
|
||||
where T : HitObject
|
||||
{
|
||||
private event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||
private event Action<HitObject, IEnumerable<HitObject>> objectConverted;
|
||||
|
||||
event Action<HitObject, IEnumerable<HitObject>> IBeatmapConverter.ObjectConverted
|
||||
{
|
||||
add => ObjectConverted += value;
|
||||
remove => ObjectConverted -= value;
|
||||
add => objectConverted += value;
|
||||
remove => objectConverted -= value;
|
||||
}
|
||||
|
||||
public IBeatmap Beatmap { get; }
|
||||
@ -92,10 +92,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
var converted = ConvertHitObject(obj, beatmap, cancellationToken);
|
||||
|
||||
if (ObjectConverted != null)
|
||||
if (objectConverted != null)
|
||||
{
|
||||
converted = converted.ToList();
|
||||
ObjectConverted.Invoke(obj, converted);
|
||||
objectConverted.Invoke(obj, converted);
|
||||
}
|
||||
|
||||
foreach (var c in converted)
|
||||
|
@ -113,8 +113,6 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
var metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = "artist",
|
||||
Title = "title",
|
||||
Author = user,
|
||||
};
|
||||
|
||||
@ -128,7 +126,6 @@ namespace osu.Game.Beatmaps
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
Ruleset = ruleset,
|
||||
Metadata = metadata,
|
||||
Version = "difficulty"
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -174,6 +171,8 @@ namespace osu.Game.Beatmaps
|
||||
if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null))
|
||||
throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}.");
|
||||
|
||||
beatmapSet.Requery(ContextFactory);
|
||||
|
||||
// check if a set already exists with the same online id, delete if it does.
|
||||
if (beatmapSet.OnlineBeatmapSetID != null)
|
||||
{
|
||||
|
@ -67,16 +67,14 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected override void ParseLine(Beatmap beatmap, Section section, string line)
|
||||
{
|
||||
var strippedLine = StripComments(line);
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case Section.General:
|
||||
handleGeneral(strippedLine);
|
||||
handleGeneral(line);
|
||||
return;
|
||||
|
||||
case Section.Editor:
|
||||
handleEditor(strippedLine);
|
||||
handleEditor(line);
|
||||
return;
|
||||
|
||||
case Section.Metadata:
|
||||
@ -84,19 +82,19 @@ namespace osu.Game.Beatmaps.Formats
|
||||
return;
|
||||
|
||||
case Section.Difficulty:
|
||||
handleDifficulty(strippedLine);
|
||||
handleDifficulty(line);
|
||||
return;
|
||||
|
||||
case Section.Events:
|
||||
handleEvent(strippedLine);
|
||||
handleEvent(line);
|
||||
return;
|
||||
|
||||
case Section.TimingPoints:
|
||||
handleTimingPoint(strippedLine);
|
||||
handleTimingPoint(line);
|
||||
return;
|
||||
|
||||
case Section.HitObjects:
|
||||
handleHitObject(strippedLine);
|
||||
handleHitObject(line);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -471,9 +471,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private string toLegacyCustomSampleBank(HitSampleInfo hitSampleInfo)
|
||||
{
|
||||
if (hitSampleInfo == null)
|
||||
return "0";
|
||||
|
||||
if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy)
|
||||
return legacy.CustomSampleBank.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
|
@ -36,6 +36,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (ShouldSkipLine(line))
|
||||
continue;
|
||||
|
||||
line = StripComments(line).TrimEnd();
|
||||
|
||||
if (line.StartsWith('[') && line.EndsWith(']'))
|
||||
{
|
||||
if (!Enum.TryParse(line[1..^1], out section))
|
||||
@ -71,8 +73,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected virtual void ParseLine(T output, Section section, string line)
|
||||
{
|
||||
line = StripComments(line);
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case Section.Colours:
|
||||
|
@ -45,8 +45,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
protected override void ParseLine(Storyboard storyboard, Section section, string line)
|
||||
{
|
||||
line = StripComments(line);
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case Section.General:
|
||||
|
@ -462,6 +462,8 @@ namespace osu.Game.Database
|
||||
// Dereference the existing file info, since the file model will be removed.
|
||||
if (file.FileInfo != null)
|
||||
{
|
||||
file.Requery(usage.Context);
|
||||
|
||||
Files.Dereference(file.FileInfo);
|
||||
|
||||
// This shouldn't be required, but here for safety in case the provided TModel is not being change tracked
|
||||
@ -635,10 +637,12 @@ namespace osu.Game.Database
|
||||
{
|
||||
using (Stream s = reader.GetStream(file))
|
||||
{
|
||||
var fileInfo = files.Add(s);
|
||||
fileInfos.Add(new TFileModel
|
||||
{
|
||||
Filename = file.Substring(prefix.Length).ToStandardisedPath(),
|
||||
FileInfo = files.Add(s)
|
||||
FileInfo = fileInfo,
|
||||
FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
71
osu.Game/Database/DatabaseWorkaroundExtensions.cs
Normal file
71
osu.Game/Database/DatabaseWorkaroundExtensions.cs
Normal file
@ -0,0 +1,71 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods which contain workarounds to make EFcore 5.x work with our existing (incorrect) thread safety.
|
||||
/// The intention is to avoid blocking package updates while we consider the future of the database backend, with a potential backend switch imminent.
|
||||
/// </summary>
|
||||
public static class DatabaseWorkaroundExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Re-query the provided model to ensure it is in a sane state. This method requires explicit implementation per model type.
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="contextFactory"></param>
|
||||
public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
switch (model)
|
||||
{
|
||||
case SkinInfo skinInfo:
|
||||
requeryFiles(skinInfo.Files, contextFactory);
|
||||
break;
|
||||
|
||||
case ScoreInfo scoreInfo:
|
||||
requeryFiles(scoreInfo.Beatmap.BeatmapSet.Files, contextFactory);
|
||||
requeryFiles(scoreInfo.Files, contextFactory);
|
||||
break;
|
||||
|
||||
case BeatmapSetInfo beatmapSetInfo:
|
||||
var context = contextFactory.Get();
|
||||
|
||||
foreach (var beatmap in beatmapSetInfo.Beatmaps)
|
||||
{
|
||||
// Workaround System.InvalidOperationException
|
||||
// The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked.
|
||||
beatmap.Ruleset = context.RulesetInfo.Find(beatmap.RulesetID);
|
||||
}
|
||||
|
||||
requeryFiles(beatmapSetInfo.Files, contextFactory);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model));
|
||||
}
|
||||
|
||||
void requeryFiles<T>(List<T> files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo
|
||||
{
|
||||
var dbContext = databaseContextFactory.Get();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
Requery(file, dbContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Requery(this INamedFileInfo file, OsuDbContext dbContext)
|
||||
{
|
||||
// Workaround System.InvalidOperationException
|
||||
// The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked.
|
||||
file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Statistics;
|
||||
@ -111,10 +110,10 @@ namespace osu.Game.Database
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
optionsBuilder
|
||||
// this is required for the time being due to the way we are querying in places like BeatmapStore.
|
||||
// if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled.
|
||||
.ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning))
|
||||
.UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10))
|
||||
.UseSqlite(connectionString,
|
||||
sqliteOptions => sqliteOptions
|
||||
.CommandTimeout(10)
|
||||
.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
|
||||
.UseLoggerFactory(logger.Value);
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
@ -53,6 +54,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
CornerRadius = CORNER_RADIUS,
|
||||
};
|
||||
|
||||
public override bool AcceptsFocus => true;
|
||||
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
{
|
||||
base.OnFocus(e);
|
||||
GetContainingInputManager().ChangeFocus(Component);
|
||||
}
|
||||
|
||||
protected override OsuTextBox CreateComponent() => CreateTextBox().With(t =>
|
||||
{
|
||||
t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText);
|
||||
|
@ -886,7 +886,8 @@ namespace osu.Game
|
||||
return true;
|
||||
|
||||
case GlobalAction.ToggleGameplayMouseButtons:
|
||||
LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get<bool>(OsuSetting.MouseDisableButtons));
|
||||
var mouseDisableButtons = LocalConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||
mouseDisableButtons.Value = !mouseDisableButtons.Value;
|
||||
return true;
|
||||
|
||||
case GlobalAction.RandomSkin:
|
||||
|
@ -138,7 +138,6 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
if (!string.IsNullOrEmpty(user.Twitter))
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
|
||||
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtocol, user.Website);
|
||||
|
||||
// If no information was added to the bottomLinkContainer, hide it to avoid unwanted padding
|
||||
|
@ -23,51 +23,24 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
|
||||
{
|
||||
this.user.BindTo(user);
|
||||
CountSection total;
|
||||
CountSection avaliable;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Masking = true;
|
||||
CornerRadius = 3;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Children = new[]
|
||||
{
|
||||
total = new CountTotal(),
|
||||
avaliable = new CountAvailable()
|
||||
}
|
||||
}
|
||||
};
|
||||
this.user.ValueChanged += u =>
|
||||
{
|
||||
total.Count = u.NewValue?.Kudosu.Total ?? 0;
|
||||
avaliable.Count = u.NewValue?.Kudosu.Available ?? 0;
|
||||
};
|
||||
Child = total = new CountTotal();
|
||||
|
||||
this.user.ValueChanged += u => total.Count = u.NewValue?.Kudosu.Total ?? 0;
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e) => true;
|
||||
|
||||
private class CountAvailable : CountSection
|
||||
{
|
||||
public CountAvailable()
|
||||
: base("Kudosu Avaliable")
|
||||
{
|
||||
DescriptionText.Text = "Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet.";
|
||||
}
|
||||
}
|
||||
|
||||
private class CountTotal : CountSection
|
||||
{
|
||||
public CountTotal()
|
||||
: base("Total Kudosu Earned")
|
||||
{
|
||||
DescriptionText.AddText("Based on how much of a contribution the user has made to beatmap moderation. See ");
|
||||
DescriptionText.AddLink("this link", "https://osu.ppy.sh/wiki/Kudosu");
|
||||
DescriptionText.AddLink("this page", "https://osu.ppy.sh/wiki/Kudosu");
|
||||
DescriptionText.AddText(" for more information.");
|
||||
}
|
||||
}
|
||||
@ -80,13 +53,12 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
|
||||
|
||||
public new int Count
|
||||
{
|
||||
set => valueText.Text = value.ToString();
|
||||
set => valueText.Text = value.ToString("N0");
|
||||
}
|
||||
|
||||
public CountSection(string header)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Width = 0.5f;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Padding = new MarginPadding { Top = 10, Bottom = 20 };
|
||||
Child = new FillFlowContainer
|
||||
@ -131,7 +103,6 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
lineBackground.Colour = colourProvider.Highlight1;
|
||||
DescriptionText.Colour = colourProvider.Foreground1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
set
|
||||
{
|
||||
modsJson = null;
|
||||
modsJson = JsonConvert.SerializeObject(value.Select(m => new DeserializedMod { Acronym = m.Acronym }));
|
||||
mods = value;
|
||||
}
|
||||
}
|
||||
@ -86,16 +86,7 @@ namespace osu.Game.Scoring
|
||||
[Column("Mods")]
|
||||
public string ModsJson
|
||||
{
|
||||
get
|
||||
{
|
||||
if (modsJson != null)
|
||||
return modsJson;
|
||||
|
||||
if (mods == null)
|
||||
return null;
|
||||
|
||||
return modsJson = JsonConvert.SerializeObject(mods.Select(m => new DeserializedMod { Acronym = m.Acronym }));
|
||||
}
|
||||
get => modsJson;
|
||||
set
|
||||
{
|
||||
modsJson = value;
|
||||
|
@ -52,6 +52,11 @@ namespace osu.Game.Scoring
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
protected override void PreImport(ScoreInfo model)
|
||||
{
|
||||
model.Requery(ContextFactory);
|
||||
}
|
||||
|
||||
protected override ScoreInfo CreateModel(ArchiveReader archive)
|
||||
{
|
||||
if (archive == null)
|
||||
|
@ -113,16 +113,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (e.Repeat || !e.ControlPressed)
|
||||
return false;
|
||||
|
||||
bool runOperationFromHotkey(Func<bool> operation)
|
||||
{
|
||||
operationStarted();
|
||||
bool result = operation?.Invoke() ?? false;
|
||||
operationEnded();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.G:
|
||||
return CanReverse && OnReverse?.Invoke() == true;
|
||||
return CanReverse && runOperationFromHotkey(OnReverse);
|
||||
|
||||
case Key.H:
|
||||
return CanScaleX && OnFlip?.Invoke(Direction.Horizontal) == true;
|
||||
return CanScaleX && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Horizontal) ?? false);
|
||||
|
||||
case Key.J:
|
||||
return CanScaleY && OnFlip?.Invoke(Direction.Vertical) == true;
|
||||
return CanScaleY && runOperationFromHotkey(() => OnFlip?.Invoke(Direction.Vertical) ?? false);
|
||||
}
|
||||
|
||||
return base.OnKeyDown(e);
|
||||
|
@ -74,7 +74,6 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private string lastSavedHash;
|
||||
|
||||
private Box bottomBackground;
|
||||
private Container<EditorScreen> screenContainer;
|
||||
|
||||
private EditorScreen currentScreen;
|
||||
@ -106,26 +105,29 @@ namespace osu.Game.Screens.Edit
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, GameHost host, OsuConfigManager config)
|
||||
{
|
||||
if (Beatmap.Value is DummyWorkingBeatmap)
|
||||
var loadableBeatmap = Beatmap.Value;
|
||||
|
||||
if (loadableBeatmap is DummyWorkingBeatmap)
|
||||
{
|
||||
isNewBeatmap = true;
|
||||
|
||||
var newBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value);
|
||||
loadableBeatmap = beatmapManager.CreateNew(Ruleset.Value, api.LocalUser.Value);
|
||||
|
||||
// required so we can get the track length in EditorClock.
|
||||
// this is safe as nothing has yet got a reference to this new beatmap.
|
||||
loadableBeatmap.LoadTrack();
|
||||
|
||||
// this is a bit haphazard, but guards against setting the lease Beatmap bindable if
|
||||
// the editor has already been exited.
|
||||
if (!ValidForPush)
|
||||
return;
|
||||
|
||||
// this probably shouldn't be set in the asynchronous load method, but everything following relies on it.
|
||||
Beatmap.Value = newBeatmap;
|
||||
}
|
||||
|
||||
beatDivisor.Value = Beatmap.Value.BeatmapInfo.BeatDivisor;
|
||||
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
||||
beatDivisor.Value = loadableBeatmap.BeatmapInfo.BeatDivisor;
|
||||
beatDivisor.BindValueChanged(divisor => loadableBeatmap.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
||||
|
||||
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
|
||||
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
||||
clock = new EditorClock(loadableBeatmap, beatDivisor) { IsCoupled = false };
|
||||
|
||||
UpdateClockSource();
|
||||
|
||||
@ -139,7 +141,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
try
|
||||
{
|
||||
playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
|
||||
playableBeatmap = loadableBeatmap.GetPlayableBeatmap(loadableBeatmap.BeatmapInfo.Ruleset);
|
||||
|
||||
// clone these locally for now to avoid incurring overhead on GetPlayableBeatmap usages.
|
||||
// eventually we will want to improve how/where this is done as there are issues with *not* cloning it in all cases.
|
||||
@ -153,13 +155,21 @@ namespace osu.Game.Screens.Edit
|
||||
return;
|
||||
}
|
||||
|
||||
AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin));
|
||||
AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, loadableBeatmap.Skin));
|
||||
dependencies.CacheAs(editorBeatmap);
|
||||
changeHandler = new EditorChangeHandler(editorBeatmap);
|
||||
dependencies.CacheAs<IEditorChangeHandler>(changeHandler);
|
||||
|
||||
updateLastSavedHash();
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
// we need to avoid changing the beatmap from an asynchronous load thread. it can potentially cause weirdness including crashes.
|
||||
// this assumes that nothing during the rest of this load() method is accessing Beatmap.Value (loadableBeatmap should be preferred).
|
||||
// generally this is quite safe, as the actual load of editor content comes after menuBar.Mode.ValueChanged is fired in its own LoadComplete.
|
||||
Beatmap.Value = loadableBeatmap;
|
||||
});
|
||||
|
||||
OsuMenuItem undoMenuItem;
|
||||
OsuMenuItem redoMenuItem;
|
||||
|
||||
@ -167,17 +177,6 @@ namespace osu.Game.Screens.Edit
|
||||
EditorMenuItem copyMenuItem;
|
||||
EditorMenuItem pasteMenuItem;
|
||||
|
||||
var fileMenuItems = new List<MenuItem>
|
||||
{
|
||||
new EditorMenuItem("Save", MenuItemType.Standard, Save)
|
||||
};
|
||||
|
||||
if (RuntimeInfo.IsDesktop)
|
||||
fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap));
|
||||
|
||||
fileMenuItems.Add(new EditorMenuItemSpacer());
|
||||
fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit));
|
||||
|
||||
AddInternal(new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -209,7 +208,7 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
new MenuItem("File")
|
||||
{
|
||||
Items = fileMenuItems
|
||||
Items = createFileMenuItems()
|
||||
},
|
||||
new MenuItem("Edit")
|
||||
{
|
||||
@ -242,7 +241,11 @@ namespace osu.Game.Screens.Edit
|
||||
Height = 60,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
bottomBackground = new Box { RelativeSizeAxes = Axes.Both },
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Gray2
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -299,8 +302,6 @@ namespace osu.Game.Screens.Edit
|
||||
clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue));
|
||||
|
||||
menuBar.Mode.ValueChanged += onModeChanged;
|
||||
|
||||
bottomBackground.Colour = colours.Gray2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -681,6 +682,21 @@ namespace osu.Game.Screens.Edit
|
||||
lastSavedHash = changeHandler.CurrentStateHash;
|
||||
}
|
||||
|
||||
private List<MenuItem> createFileMenuItems()
|
||||
{
|
||||
var fileMenuItems = new List<MenuItem>
|
||||
{
|
||||
new EditorMenuItem("Save", MenuItemType.Standard, Save)
|
||||
};
|
||||
|
||||
if (RuntimeInfo.IsDesktop)
|
||||
fileMenuItems.Add(new EditorMenuItem("Export package", MenuItemType.Standard, exportBeatmap));
|
||||
|
||||
fileMenuItems.Add(new EditorMenuItemSpacer());
|
||||
fileMenuItems.Add(new EditorMenuItem("Exit", MenuItemType.Standard, this.Exit));
|
||||
return fileMenuItems;
|
||||
}
|
||||
|
||||
public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);
|
||||
|
||||
public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);
|
||||
|
@ -56,6 +56,14 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
item.OnCommit += onCommit;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (string.IsNullOrEmpty(artistTextBox.Current.Value))
|
||||
GetContainingInputManager().ChangeFocus(artistTextBox);
|
||||
}
|
||||
|
||||
private void onCommit(TextBox sender, bool newText)
|
||||
{
|
||||
if (!newText) return;
|
||||
|
@ -31,8 +31,6 @@ namespace osu.Game.Skinning
|
||||
|
||||
protected override void ParseLine(List<LegacyManiaSkinConfiguration> output, Section section, string line)
|
||||
{
|
||||
line = StripComments(line);
|
||||
|
||||
switch (section)
|
||||
{
|
||||
case Section.Mania:
|
||||
|
@ -142,6 +142,11 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
protected override void PreImport(SkinInfo model)
|
||||
{
|
||||
model.Requery(ContextFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/>
|
||||
/// </summary>
|
||||
|
@ -111,9 +111,6 @@ namespace osu.Game.Users
|
||||
[JsonProperty(@"twitter")]
|
||||
public string Twitter;
|
||||
|
||||
[JsonProperty(@"skype")]
|
||||
public string Skype;
|
||||
|
||||
[JsonProperty(@"discord")]
|
||||
public string Discord;
|
||||
|
||||
|
@ -24,10 +24,10 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.309.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />
|
||||
|
@ -90,8 +90,6 @@
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<PackageReference Include="DiffPlex" Version="1.6.3" />
|
||||
<PackageReference Include="Humanizer" Version="2.8.26" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.309.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.1" />
|
||||
|
Loading…
Reference in New Issue
Block a user