1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:12:54 +08:00

Merge branch 'master' into beatmap-listing-enum-localisation

This commit is contained in:
Bartłomiej Dach 2021-06-16 13:16:58 +02:00
commit 69a474e4ab
16 changed files with 412 additions and 27 deletions

View File

@ -14,8 +14,8 @@
"jb"
]
},
"nvika": {
"version": "2.0.0",
"smoogipoo.nvika": {
"version": "1.0.1",
"commands": [
"nvika"
]

93
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,93 @@
on: [push, pull_request]
name: Continuous Integration
jobs:
test:
name: Test
runs-on: ${{matrix.os.fullname}}
env:
OSU_EXECUTION_MODE: ${{matrix.threadingMode}}
strategy:
fail-fast: false
matrix:
os:
- { prettyname: Windows, fullname: windows-latest }
- { prettyname: macOS, fullname: macos-latest }
- { prettyname: Linux, fullname: ubuntu-latest }
threadingMode: ['SingleThread', 'MultiThreaded']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install .NET 5.0.x
uses: actions/setup-dotnet@v1
with:
dotnet-version: "5.0.x"
# FIXME: libavformat is not included in Ubuntu. Let's fix that.
# https://github.com/ppy/osu-framework/issues/4349
# Remove this once https://github.com/actions/virtual-environments/issues/3306 has been resolved.
- name: Install libavformat-dev
if: ${{matrix.os.fullname == 'ubuntu-latest'}}
run: |
sudo apt-get update && \
sudo apt-get -y install libavformat-dev
- name: Compile
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
- name: Test
run: dotnet test $pwd/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx"
shell: pwsh
# Attempt to upload results even if test fails.
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always
- name: Upload Test Results
uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx
inspect-code:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# FIXME: Tools won't run in .NET 5.0 unless you install 3.1.x LTS side by side.
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
- name: Install .NET 3.1.x LTS
uses: actions/setup-dotnet@v1
with:
dotnet-version: "3.1.x"
- name: Install .NET 5.0.x
uses: actions/setup-dotnet@v1
with:
dotnet-version: "5.0.x"
- name: Restore Tools
run: dotnet tool restore
- name: Restore Packages
run: dotnet restore
- name: CodeFileSanity
run: |
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
# FIXME: Suppress warnings from templates project
dotnet codefilesanity | while read -r line; do
echo "::warning::$line"
done
# Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded.
# - name: .NET Format (Dry Run)
# run: dotnet format --dry-run --check
- name: InspectCode
run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --output=$(pwd)/inspectcodereport.xml --cachesDir=$(pwd)/inspectcode --verbosity=WARN
- name: NVika
run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors

31
.github/workflows/report-nunit.yml vendored Normal file
View File

@ -0,0 +1,31 @@
# This is a workaround to allow PRs to report their coverage. This will run inside the base repository.
# See:
# * https://github.com/dorny/test-reporter#recommended-setup-for-public-repositories
# * https://docs.github.com/en/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token
name: Annotate CI run with test results
on:
workflow_run:
workflows: ["Continuous Integration"]
types:
- completed
jobs:
annotate:
name: Annotate CI run with test results
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion != 'cancelled' }}
strategy:
fail-fast: false
matrix:
os:
- { prettyname: Windows }
- { prettyname: macOS }
- { prettyname: Linux }
threadingMode: ['SingleThread', 'MultiThreaded']
steps:
- name: Annotate CI run with test results
uses: dorny/test-reporter@v1.4.2
with:
artifact: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}})
path: "*.trx"
reporter: dotnet-trx

View File

@ -0,0 +1,102 @@
// 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.Collections.Generic;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModApproachDifferent : Mod, IApplicableToDrawableHitObjects
{
public override string Name => "Approach Different";
public override string Acronym => "AD";
public override string Description => "Never trust the approach circles...";
public override double ScoreMultiplier => 1;
public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
[SettingSource("Initial size", "Change the initial size of the approach circle, relative to hit circles.", 0)]
public BindableFloat Scale { get; } = new BindableFloat(4)
{
Precision = 0.1f,
MinValue = 2,
MaxValue = 10,
};
[SettingSource("Style", "Change the animation style of the approach circles.", 1)]
public Bindable<AnimationStyle> Style { get; } = new Bindable<AnimationStyle>();
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
drawables.ForEach(drawable =>
{
drawable.ApplyCustomUpdateState += (drawableObject, state) =>
{
if (!(drawableObject is DrawableHitCircle drawableHitCircle)) return;
var hitCircle = drawableHitCircle.HitObject;
drawableHitCircle.ApproachCircle.ClearTransforms(targetMember: nameof(Scale));
using (drawableHitCircle.BeginAbsoluteSequence(hitCircle.StartTime - hitCircle.TimePreempt))
drawableHitCircle.ApproachCircle.ScaleTo(Scale.Value).ScaleTo(1f, hitCircle.TimePreempt, getEasing(Style.Value));
};
});
}
private Easing getEasing(AnimationStyle style)
{
switch (style)
{
default:
return Easing.None;
case AnimationStyle.Accelerate1:
return Easing.In;
case AnimationStyle.Accelerate2:
return Easing.InCubic;
case AnimationStyle.Accelerate3:
return Easing.InQuint;
case AnimationStyle.Gravity:
return Easing.InBack;
case AnimationStyle.Decelerate1:
return Easing.Out;
case AnimationStyle.Decelerate2:
return Easing.OutCubic;
case AnimationStyle.Decelerate3:
return Easing.OutQuint;
case AnimationStyle.InOut1:
return Easing.InOutCubic;
case AnimationStyle.InOut2:
return Easing.InOutQuint;
}
}
public enum AnimationStyle
{
Gravity,
InOut1,
InOut2,
Accelerate1,
Accelerate2,
Accelerate3,
Decelerate1,
Decelerate2,
Decelerate3,
}
}
}

View File

@ -187,6 +187,7 @@ namespace osu.Game.Rulesets.Osu
new MultiMod(new ModWindUp(), new ModWindDown()),
new OsuModTraceable(),
new OsuModBarrelRoll(),
new OsuModApproachDifferent(),
};
case ModType.System:

View File

@ -161,15 +161,18 @@ namespace osu.Game.Tests.Visual.Background
private void loadNextBackground()
{
SeasonalBackground previousBackground = null;
SeasonalBackground background = null;
AddStep("create next background", () =>
{
previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
background = backgroundLoader.LoadNextBackground();
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
});
AddUntilStep("background loaded", () => background.IsLoaded);
AddAssert("background is different", () => !background.Equals(previousBackground));
}
private void assertAnyBackground()

View File

@ -101,10 +101,20 @@ namespace osu.Game.Beatmaps
/// Rulesets ordered descending by their respective recommended difficulties.
/// The currently selected ruleset will always be first.
/// </returns>
private IEnumerable<RulesetInfo> orderedRulesets =>
recommendedDifficultyMapping
.OrderByDescending(pair => pair.Value).Select(pair => pair.Key).Where(r => !r.Equals(ruleset.Value))
.Prepend(ruleset.Value);
private IEnumerable<RulesetInfo> orderedRulesets
{
get
{
if (LoadState < LoadState.Ready || ruleset.Value == null)
return Enumerable.Empty<RulesetInfo>();
return recommendedDifficultyMapping
.OrderByDescending(pair => pair.Value)
.Select(pair => pair.Key)
.Where(r => !r.Equals(ruleset.Value))
.Prepend(ruleset.Value);
}
}
private void onlineStateChanged(ValueChangedEvent<APIState> state) => Schedule(() =>
{

View File

@ -0,0 +1,33 @@
// 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.Globalization;
using osu.Game.Localisation;
namespace osu.Game.Extensions
{
/// <summary>
/// Conversion utilities for the <see cref="Language"/> enum.
/// </summary>
public static class LanguageExtensions
{
/// <summary>
/// Returns the culture code of the <see cref="CultureInfo"/> that corresponds to the supplied <paramref name="language"/>.
/// </summary>
/// <remarks>
/// This is required as enum member names are not allowed to contain hyphens.
/// </remarks>
public static string ToCultureCode(this Language language)
=> language.ToString().Replace("_", "-");
/// <summary>
/// Attempts to parse the supplied <paramref name="cultureCode"/> to a <see cref="Language"/> value.
/// </summary>
/// <param name="cultureCode">The code of the culture to parse.</param>
/// <param name="language">The parsed <see cref="Language"/>. Valid only if the return value of the method is <see langword="true" />.</param>
/// <returns>Whether the parsing succeeded.</returns>
public static bool TryParseCultureCode(string cultureCode, out Language language)
=> Enum.TryParse(cultureCode.Replace("-", "_"), out language);
}
}

View File

@ -99,5 +99,14 @@ namespace osu.Game.Graphics.Backgrounds
// ensure we're not loading in without a transition.
this.FadeInFromZero(200, Easing.InOutSine);
}
public override bool Equals(Background other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.GetType() == GetType()
&& ((SeasonalBackground)other).url == url;
}
}
}

View File

@ -10,7 +10,104 @@ namespace osu.Game.Localisation
[Description(@"English")]
en,
// TODO: Requires Arabic glyphs to be added to resources (and possibly also RTL support).
// [Description(@"اَلْعَرَبِيَّةُ")]
// ar,
// TODO: Some accented glyphs are missing. Revisit when adding Inter.
// [Description(@"Беларуская мова")]
// be,
[Description(@"Български")]
bg,
[Description(@"Česky")]
cs,
[Description(@"Dansk")]
da,
[Description(@"Deutsch")]
de,
// TODO: Some accented glyphs are missing. Revisit when adding Inter.
// [Description(@"Ελληνικά")]
// el,
[Description(@"español")]
es,
[Description(@"Suomi")]
fi,
[Description(@"français")]
fr,
[Description(@"Magyar")]
hu,
[Description(@"Bahasa Indonesia")]
id,
[Description(@"Italiano")]
it,
[Description(@"日本語")]
ja
ja,
[Description(@"한국어")]
ko,
[Description(@"Nederlands")]
nl,
[Description(@"Norsk")]
no,
[Description(@"polski")]
pl,
[Description(@"Português")]
pt,
[Description(@"Português (Brasil)")]
pt_br,
[Description(@"Română")]
ro,
[Description(@"Русский")]
ru,
[Description(@"Slovenčina")]
sk,
[Description(@"Svenska")]
sv,
[Description(@"ไทย")]
th,
[Description(@"Tagalog")]
tl,
[Description(@"Türkçe")]
tr,
// TODO: Some accented glyphs are missing. Revisit when adding Inter.
// [Description(@"Українська мова")]
// uk,
[Description(@"Tiếng Việt")]
vi,
[Description(@"简体中文")]
zh,
[Description(@"繁體中文(香港)")]
zh_hk,
[Description(@"繁體中文(台灣)")]
zh_tw
}
}

View File

@ -50,6 +50,7 @@ using osu.Game.Updater;
using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel;
using osu.Game.Database;
using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Localisation;
using osu.Game.Skinning.Editor;
@ -426,9 +427,12 @@ namespace osu.Game
{
// The given ScoreInfo may have missing properties if it was retrieved from online data. Re-retrieve it from the database
// to ensure all the required data for presenting a replay are present.
var databasedScoreInfo = score.OnlineScoreID != null
? ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID)
: ScoreManager.Query(s => s.Hash == score.Hash);
ScoreInfo databasedScoreInfo = null;
if (score.OnlineScoreID != null)
databasedScoreInfo = ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID);
databasedScoreInfo ??= ScoreManager.Query(s => s.Hash == score.Hash);
if (databasedScoreInfo == null)
{
@ -577,7 +581,7 @@ namespace osu.Game
foreach (var language in Enum.GetValues(typeof(Language)).OfType<Language>())
{
var cultureCode = language.ToString();
var cultureCode = language.ToCultureCode();
Localisation.AddLanguage(cultureCode, new ResourceManagerLocalisationStore(cultureCode));
}
@ -712,7 +716,6 @@ namespace osu.Game
PostNotification = n => notifications.Post(n),
}, Add, true);
loadComponentSingleFile(difficultyRecommender, Add);
loadComponentSingleFile(stableImportManager, Add);
loadComponentSingleFile(screenshotManager, Add);
@ -755,6 +758,7 @@ namespace osu.Game
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
Add(difficultyRecommender);
Add(externalLinkOpener = new ExternalLinkOpener());
Add(new MusicKeyBindingHandler());

View File

@ -18,6 +18,7 @@ using JetBrains.Annotations;
using System;
using osu.Framework.Extensions;
using osu.Framework.Localisation;
using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays
{
@ -54,7 +55,7 @@ namespace osu.Game.Overlays
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Text = @"Sort by"
Text = SortStrings.Default
},
CreateControl().With(c =>
{

View File

@ -1,11 +1,11 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Extensions;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.General
@ -35,11 +35,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
},
};
if (!Enum.TryParse<Language>(frameworkLocale.Value, out var locale))
if (!LanguageExtensions.TryParseCultureCode(frameworkLocale.Value, out var locale))
locale = Language.en;
languageSelection.Current.Value = locale;
languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToString());
languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode());
}
}
}

View File

@ -19,6 +19,8 @@ namespace osu.Game.Overlays.Settings
Margin = new MarginPadding { Top = 5 };
RelativeSizeAxes = Axes.X;
}
protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200);
}
}
}

View File

@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// </remarks>
public double TimeAtPosition(float localPosition, double currentTime)
{
float scrollPosition = axisInverted ? scrollLength - localPosition : localPosition;
float scrollPosition = axisInverted ? -localPosition : localPosition;
return scrollingInfo.Algorithm.TimeAt(scrollPosition, currentTime, timeRange.Value, scrollLength);
}
@ -81,8 +81,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// </remarks>
public double TimeAtScreenSpacePosition(Vector2 screenSpacePosition)
{
Vector2 localPosition = ToLocalSpace(screenSpacePosition);
return TimeAtPosition(scrollingAxis == Direction.Horizontal ? localPosition.X : localPosition.Y, Time.Current);
Vector2 pos = ToLocalSpace(screenSpacePosition);
float localPosition = scrollingAxis == Direction.Horizontal ? pos.X : pos.Y;
localPosition -= axisInverted ? scrollLength : 0;
return TimeAtPosition(localPosition, Time.Current);
}
/// <summary>
@ -91,7 +93,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
public float PositionAtTime(double time, double currentTime)
{
float scrollPosition = scrollingInfo.Algorithm.PositionAt(time, currentTime, timeRange.Value, scrollLength);
return axisInverted ? scrollLength - scrollPosition : scrollPosition;
return axisInverted ? -scrollPosition : scrollPosition;
}
/// <summary>
@ -106,6 +108,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
public Vector2 ScreenSpacePositionAtTime(double time)
{
float localPosition = PositionAtTime(time, Time.Current);
localPosition += axisInverted ? scrollLength : 0;
return scrollingAxis == Direction.Horizontal
? ToScreenSpace(new Vector2(localPosition, DrawHeight / 2))
: ToScreenSpace(new Vector2(DrawWidth / 2, localPosition));
@ -236,14 +239,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
{
float position = PositionAtTime(hitObject.HitObject.StartTime, currentTime);
// The position returned from `PositionAtTime` is assuming the `TopLeft` anchor.
// A correction is needed because the hit objects are using a different anchor for each direction (e.g. `BottomCentre` for `Bottom` direction).
float anchorCorrection = axisInverted ? scrollLength : 0;
if (scrollingAxis == Direction.Horizontal)
hitObject.X = position - anchorCorrection;
hitObject.X = position;
else
hitObject.Y = position - anchorCorrection;
hitObject.Y = position;
}
}
}

View File

@ -350,7 +350,7 @@ namespace osu.Game.Tests.Visual
if (CurrentTime >= Length)
{
Stop();
RaiseCompleted();
// `RaiseCompleted` is not called here to prevent transitioning to the next song.
}
}
}