1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-19 10:43:21 +08:00

Merge branch 'master' into settings-source-value-changed

This commit is contained in:
Dan Balasescu 2019-12-25 17:23:08 +09:00 committed by GitHub
commit a265a0c7c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 328 additions and 65 deletions

View File

@ -54,6 +54,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1219.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1224.0" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,8 @@
// 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.Text;
using DiscordRPC;
using DiscordRPC.Message;
using osu.Framework.Allocation;
@ -43,6 +45,10 @@ namespace osu.Desktop
};
client.OnReady += onReady;
// safety measure for now, until we performance test / improve backoff for failed connections.
client.OnConnectionFailed += (_, __) => client.Deinitialize();
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
@ -77,8 +83,8 @@ namespace osu.Desktop
if (status.Value is UserStatusOnline && activity.Value != null)
{
presence.State = activity.Value.Status;
presence.Details = getDetails(activity.Value);
presence.State = truncate(activity.Value.Status);
presence.Details = truncate(getDetails(activity.Value));
}
else
{
@ -96,6 +102,27 @@ namespace osu.Desktop
client.SetPresence(presence);
}
private static readonly int ellipsis_length = Encoding.UTF8.GetByteCount(new[] { '…' });
private string truncate(string str)
{
if (Encoding.UTF8.GetByteCount(str) <= 128)
return str;
ReadOnlyMemory<char> strMem = str.AsMemory();
do
{
strMem = strMem[..^1];
} while (Encoding.UTF8.GetByteCount(strMem.Span) + ellipsis_length > 128);
return string.Create(strMem.Length + 1, strMem, (span, mem) =>
{
mem.Span.CopyTo(span);
span[^1] = '…';
});
}
private string getDetails(UserActivity activity)
{
switch (activity)

View File

@ -0,0 +1,155 @@
// 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 System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneLegacyBeatmapSkin : OsuTestScene
{
[Resolved]
private AudioManager audio { get; set; }
[TestCase(true)]
[TestCase(false)]
public void TestBeatmapComboColours(bool customSkinColoursPresent)
{
ExposedPlayer player = null;
AddStep("load coloured beatmap", () => player = loadBeatmap(customSkinColoursPresent, true));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
}
[Test]
public void TestBeatmapNoComboColours()
{
ExposedPlayer player = null;
AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
[Test]
public void TestBeatmapNoComboColoursSkinOverride()
{
ExposedPlayer player = null;
AddStep("load custom-skin colour", () => player = loadBeatmap(true, false));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours)
{
ExposedPlayer player;
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours);
Child = new OsuScreenStack(player = new ExposedPlayer(userHasCustomColours)) { RelativeSizeAxes = Axes.Both };
return player;
}
private class ExposedPlayer : Player
{
private readonly bool userHasCustomColours;
public ExposedPlayer(bool userHasCustomColours)
: base(false, false)
{
this.userHasCustomColours = userHasCustomColours;
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<ISkinSource>(new TestSkin(userHasCustomColours));
return dependencies;
}
public IReadOnlyList<Color4> UsableComboColours =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
}
private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
{
private readonly bool hasColours;
public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours)
: base(new Beatmap
{
BeatmapInfo =
{
BeatmapSet = new BeatmapSetInfo(),
Ruleset = new OsuRuleset().RulesetInfo,
},
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
}, null, null, audio)
{
this.hasColours = hasColours;
}
protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours);
}
private class TestBeatmapSkin : LegacyBeatmapSkin
{
public static Color4[] Colours { get; } =
{
new Color4(50, 100, 150, 255),
new Color4(40, 80, 120, 255),
};
public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours)
: base(beatmap, new ResourceStore<byte[]>(), null)
{
if (hasColours)
Configuration.AddComboColours(Colours);
}
}
private class TestSkin : LegacySkin, ISkinSource
{
public static Color4[] Colours { get; } =
{
new Color4(150, 100, 50, 255),
new Color4(20, 20, 20, 255),
};
public TestSkin(bool hasCustomColours)
: base(new SkinInfo(), null, null, string.Empty)
{
if (hasCustomColours)
Configuration.AddComboColours(Colours);
}
public event Action SourceChanged
{
add { }
remove { }
}
}
}
}

View File

@ -130,7 +130,7 @@ namespace osu.Game.Tests.Gameplay
switch (global)
{
case GlobalSkinConfiguration.ComboColours:
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(ComboColours));
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(ComboColours));
}
break;

View File

@ -13,31 +13,22 @@ namespace osu.Game.Tests.Skins
[TestFixture]
public class LegacySkinDecoderTest
{
[TestCase(true)]
[TestCase(false)]
public void TestDecodeSkinColours(bool hasColours)
[Test]
public void TestDecodeSkinColours()
{
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini"))
using (var resStream = TestResources.OpenResource("skin.ini"))
using (var stream = new LineBufferedReader(resStream))
{
var comboColors = decoder.Decode(stream).ComboColours;
List<Color4> expectedColors;
if (hasColours)
var expectedColors = new List<Color4>
{
expectedColors = new List<Color4>
{
new Color4(142, 199, 255, 255),
new Color4(255, 128, 128, 255),
new Color4(128, 255, 255, 255),
new Color4(100, 100, 100, 100),
};
}
else
expectedColors = new DefaultSkin().Configuration.ComboColours;
new Color4(142, 199, 255, 255),
new Color4(255, 128, 128, 255),
new Color4(128, 255, 255, 255),
new Color4(100, 100, 100, 100),
};
Assert.AreEqual(expectedColors.Count, comboColors.Count);
for (int i = 0; i < expectedColors.Count; i++)
@ -45,6 +36,37 @@ namespace osu.Game.Tests.Skins
}
}
[Test]
public void TestDecodeEmptySkinColours()
{
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
using (var stream = new LineBufferedReader(resStream))
{
var comboColors = decoder.Decode(stream).ComboColours;
var expectedColors = SkinConfiguration.DefaultComboColours;
Assert.AreEqual(expectedColors.Count, comboColors.Count);
for (int i = 0; i < expectedColors.Count; i++)
Assert.AreEqual(expectedColors[i], comboColors[i]);
}
}
[Test]
public void TestDecodeEmptySkinColoursNoFallback()
{
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource("skin-empty.ini"))
using (var stream = new LineBufferedReader(resStream))
{
var skinConfiguration = decoder.Decode(stream);
skinConfiguration.AllowDefaultComboColoursFallback = false;
Assert.IsNull(skinConfiguration.ComboColours);
}
}
[Test]
public void TestDecodeGeneral()
{

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
@ -21,8 +22,8 @@ namespace osu.Game.Tests.Skins
[HeadlessTest]
public class TestSceneSkinConfigurationLookup : OsuTestScene
{
private LegacySkin source1;
private LegacySkin source2;
private SkinSource source1;
private SkinSource source2;
private SkinRequester requester;
[SetUp]
@ -94,7 +95,7 @@ namespace osu.Game.Tests.Skins
[Test]
public void TestGlobalLookup()
{
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
}
[Test]
@ -116,6 +117,28 @@ namespace osu.Game.Tests.Skins
});
}
[Test]
public void TestEmptyComboColours()
{
AddAssert("Check retrieved combo colours is skin default colours", () =>
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(SkinConfiguration.DefaultComboColours) ?? false);
}
[Test]
public void TestEmptyComboColoursNoFallback()
{
AddStep("Add custom combo colours to source1", () => source1.Configuration.AddComboColours(
new Color4(100, 150, 200, 255),
new Color4(55, 110, 166, 255),
new Color4(75, 125, 175, 255)
));
AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false);
AddAssert("Check retrieved combo colours from source1", () =>
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
}
[Test]
public void TestLegacyVersionLookup()
{

View File

@ -8,6 +8,14 @@ namespace osu.Game.Beatmaps.Formats
{
public interface IHasComboColours
{
List<Color4> ComboColours { get; set; }
/// <summary>
/// Retrieves the list of combo colours for presentation only.
/// </summary>
IReadOnlyList<Color4> ComboColours { get; }
/// <summary>
/// Adds combo colours to the list.
/// </summary>
void AddComboColours(params Color4[] colours);
}
}

View File

@ -77,8 +77,6 @@ namespace osu.Game.Beatmaps.Formats
return line;
}
private bool hasComboColours;
private void handleColours(T output, string line)
{
var pair = SplitKeyVal(line);
@ -105,14 +103,7 @@ namespace osu.Game.Beatmaps.Formats
{
if (!(output is IHasComboColours tHasComboColours)) return;
if (!hasComboColours)
{
// remove default colours.
tHasComboColours.ComboColours.Clear();
hasComboColours = true;
}
tHasComboColours.ComboColours.Add(colour);
tHasComboColours.AddComboColours(colour);
}
else
{

View File

@ -12,10 +12,12 @@ using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
public class OsuTextBox : TextBox
public class OsuTextBox : BasicTextBox
{
protected override float LeftRightPadding => 10;
protected override float CaretWidth => 3;
protected override SpriteText CreatePlaceholder() => new OsuSpriteText
{
Font = OsuFont.GetFont(italics: true),
@ -41,6 +43,8 @@ namespace osu.Game.Graphics.UserInterface
BackgroundCommit = BorderColour = colour.Yellow;
}
protected override Color4 SelectionColour => new Color4(249, 90, 255, 255);
protected override void OnFocus(FocusEvent e)
{
BorderThickness = 3;

View File

@ -356,7 +356,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
if (HitObject is IHasComboInformation combo)
{
var comboColours = CurrentSkin.GetConfig<GlobalSkinConfiguration, List<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
var comboColours = CurrentSkin.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
AccentColour.Value = comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
}
}

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
@ -132,6 +133,8 @@ namespace osu.Game.Screens.Menu
private void confirmAndExit()
{
if (exitConfirmed) return;
exitConfirmed = true;
this.Exit();
}
@ -244,10 +247,18 @@ namespace osu.Game.Screens.Menu
public override bool OnExiting(IScreen next)
{
if (!exitConfirmed && dialogOverlay != null && !(dialogOverlay.CurrentDialog is ConfirmExitDialog))
if (!exitConfirmed && dialogOverlay != null)
{
dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort()));
return true;
if (dialogOverlay.CurrentDialog is ConfirmExitDialog exitDialog)
{
exitConfirmed = true;
exitDialog.Buttons.First().Click();
}
else
{
dialogOverlay.Push(new ConfirmExitDialog(confirmAndExit, () => exitConfirmOverlay.Abort()));
return true;
}
}
buttons.State = ButtonSystemState.Exit;

View File

@ -562,7 +562,7 @@ namespace osu.Game.Screens.Play
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
// as we are no longer the current screen, we cannot guarantee the track is still usable.
GameplayClockContainer.StopUsingBeatmapClock();
GameplayClockContainer?.StopUsingBeatmapClock();
fadeOut();
return base.OnExiting(next);

View File

@ -13,13 +13,12 @@ namespace osu.Game.Skinning
: base(Info, storage, audioManager, string.Empty)
{
Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255);
Configuration.ComboColours.AddRange(new[]
{
Configuration.AddComboColours(
new Color4(255, 192, 0, 255),
new Color4(0, 202, 0, 255),
new Color4(18, 124, 255, 255),
new Color4(242, 24, 57, 255),
});
new Color4(242, 24, 57, 255)
);
Configuration.LegacyVersion = 2.0m;
}

View File

@ -35,7 +35,7 @@ namespace osu.Game.Skinning
switch (global)
{
case GlobalSkinConfiguration.ComboColours:
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(Configuration.ComboColours));
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(Configuration.ComboColours));
}
break;

View File

@ -1,8 +1,6 @@
// 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 osuTK.Graphics;
namespace osu.Game.Skinning
{
/// <summary>
@ -10,15 +8,5 @@ namespace osu.Game.Skinning
/// </summary>
public class DefaultSkinConfiguration : SkinConfiguration
{
public DefaultSkinConfiguration()
{
ComboColours.AddRange(new[]
{
new Color4(255, 192, 0, 255),
new Color4(0, 202, 0, 255),
new Color4(18, 124, 255, 255),
new Color4(242, 24, 57, 255),
});
}
}
}

View File

@ -12,6 +12,8 @@ namespace osu.Game.Skinning
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
{
// Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer)
Configuration.AllowDefaultComboColoursFallback = false;
}
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>

View File

@ -72,7 +72,11 @@ namespace osu.Game.Skinning
switch (global)
{
case GlobalSkinConfiguration.ComboColours:
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(Configuration.ComboColours));
var comboColours = Configuration.ComboColours;
if (comboColours != null)
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(comboColours));
break;
}
break;

View File

@ -3,7 +3,7 @@
namespace osu.Game.Skinning
{
public class LegacySkinConfiguration : DefaultSkinConfiguration
public class LegacySkinConfiguration : SkinConfiguration
{
public const decimal LATEST_VERSION = 2.7m;

View File

@ -14,7 +14,36 @@ namespace osu.Game.Skinning
{
public readonly SkinInfo SkinInfo = new SkinInfo();
public List<Color4> ComboColours { get; set; } = new List<Color4>();
/// <summary>
/// Whether to allow <see cref="DefaultComboColours"/> as a fallback list for when no combo colours are provided.
/// </summary>
internal bool AllowDefaultComboColoursFallback = true;
public static List<Color4> DefaultComboColours { get; } = new List<Color4>
{
new Color4(255, 192, 0, 255),
new Color4(0, 202, 0, 255),
new Color4(18, 124, 255, 255),
new Color4(242, 24, 57, 255),
};
private readonly List<Color4> comboColours = new List<Color4>();
public IReadOnlyList<Color4> ComboColours
{
get
{
if (comboColours.Count > 0)
return comboColours;
if (AllowDefaultComboColoursFallback)
return DefaultComboColours;
return null;
}
}
public void AddComboColours(params Color4[] colours) => comboColours.AddRange(colours);
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();

View File

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1219.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1224.0" />
<PackageReference Include="Sentry" Version="1.2.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -74,7 +74,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1219.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1224.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -82,7 +82,7 @@
<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="2019.1219.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1224.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />