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

Merge branch 'master' into collection-counts

This commit is contained in:
Dean Herbert 2023-04-03 15:25:56 +09:00
commit 01e489e9c3
49 changed files with 282 additions and 312 deletions

View File

@ -13,17 +13,17 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side. # FIXME: Tools won't run in .NET 6.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 # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
- name: Install .NET 3.1.x LTS - name: Install .NET 3.1.x LTS
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: "3.1.x" dotnet-version: "3.1.x"
- name: Install .NET 6.0.x - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
@ -77,10 +77,10 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Install .NET 6.0.x - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
@ -94,7 +94,7 @@ jobs:
# Attempt to upload results even if test fails. # Attempt to upload results even if test fails.
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always
- name: Upload Test Results - name: Upload Test Results
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
if: ${{ always() }} if: ${{ always() }}
with: with:
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
@ -106,10 +106,10 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Install .NET 6.0.x - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
@ -125,10 +125,10 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Install .NET 6.0.x - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"

View File

@ -48,8 +48,8 @@ jobs:
CONTINUE="no" CONTINUE="no"
fi fi
echo "::set-output name=continue::${CONTINUE}" echo "continue=${CONTINUE}" >> $GITHUB_OUTPUT
echo "::set-output name=matrix::${MATRIX_JSON}" echo "matrix=${MATRIX_JSON}" >> $GITHUB_OUTPUT
diffcalc: diffcalc:
name: Run name: Run
runs-on: self-hosted runs-on: self-hosted
@ -80,34 +80,34 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
echo "::set-output name=branchname::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" echo "branchname=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" >> $GITHUB_OUTPUT
echo "::set-output name=repo::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" echo "repo=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" >> $GITHUB_OUTPUT
# Checkout osu # Checkout osu
- name: Checkout osu (master) - name: Checkout osu (master)
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: 'master/osu' path: 'master/osu'
- name: Checkout osu (pr) - name: Checkout osu (pr)
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: 'pr/osu' path: 'pr/osu'
repository: ${{ steps.upstreambranch.outputs.repo }} repository: ${{ steps.upstreambranch.outputs.repo }}
ref: ${{ steps.upstreambranch.outputs.branchname }} ref: ${{ steps.upstreambranch.outputs.branchname }}
- name: Checkout osu-difficulty-calculator (master) - name: Checkout osu-difficulty-calculator (master)
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
repository: ppy/osu-difficulty-calculator repository: ppy/osu-difficulty-calculator
path: 'master/osu-difficulty-calculator' path: 'master/osu-difficulty-calculator'
- name: Checkout osu-difficulty-calculator (pr) - name: Checkout osu-difficulty-calculator (pr)
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
repository: ppy/osu-difficulty-calculator repository: ppy/osu-difficulty-calculator
path: 'pr/osu-difficulty-calculator' path: 'pr/osu-difficulty-calculator'
- name: Install .NET 5.0.x - name: Install .NET 5.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v3
with: with:
dotnet-version: "5.0.x" dotnet-version: "5.0.x"

View File

@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0

View File

@ -105,7 +105,7 @@ When it comes to contributing to the project, the two main things you can do to
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web). If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so.
## Licence ## Licence

View File

@ -11,7 +11,7 @@
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger> <AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.314.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2023.403.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" /> <AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />

View File

@ -1,17 +0,0 @@
// 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.
#nullable disable
using Foundation;
using osu.Framework.iOS;
using osu.Game.Tests;
namespace osu.Game.Rulesets.Catch.Tests.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameAppDelegate
{
protected override Framework.Game CreateGame() => new OsuTestBrowser();
}
}

View File

@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using osu.Framework.iOS;
using osu.Game.Tests;
using UIKit;
namespace osu.Game.Rulesets.Catch.Tests.iOS namespace osu.Game.Rulesets.Catch.Tests.iOS
{ {
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, typeof(AppDelegate)); GameApplication.Main(new OsuTestBrowser());
} }
} }
} }

View File

@ -136,6 +136,7 @@ namespace osu.Game.Rulesets.Catch.UI
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
Size = new Vector2(BASE_SIZE); Size = new Vector2(BASE_SIZE);
if (difficulty != null) if (difficulty != null)
Scale = calculateScale(difficulty); Scale = calculateScale(difficulty);
@ -333,8 +334,11 @@ namespace osu.Game.Rulesets.Catch.UI
base.Update(); base.Update();
var scaleFromDirection = new Vector2((int)VisualDirection, 1); var scaleFromDirection = new Vector2((int)VisualDirection, 1);
body.Scale = scaleFromDirection; body.Scale = scaleFromDirection;
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; // Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
caughtObjectContainer.Scale = (1 / Scale.X) * (flipCatcherPlate ? scaleFromDirection : Vector2.One);
hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
// Correct overshooting. // Correct overshooting.
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) || if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||

View File

@ -1,17 +0,0 @@
// 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.
#nullable disable
using Foundation;
using osu.Framework.iOS;
using osu.Game.Tests;
namespace osu.Game.Rulesets.Mania.Tests.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameAppDelegate
{
protected override Framework.Game CreateGame() => new OsuTestBrowser();
}
}

View File

@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using osu.Framework.iOS;
using osu.Game.Tests;
using UIKit;
namespace osu.Game.Rulesets.Mania.Tests.iOS namespace osu.Game.Rulesets.Mania.Tests.iOS
{ {
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, typeof(AppDelegate)); GameApplication.Main(new OsuTestBrowser());
} }
} }
} }

View File

@ -3,6 +3,9 @@
#nullable disable #nullable disable
using osu.Framework.Extensions.ObjectExtensions;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
@ -10,5 +13,19 @@ namespace osu.Game.Rulesets.Mania.Tests
public partial class TestSceneManiaPlayer : PlayerTestScene public partial class TestSceneManiaPlayer : PlayerTestScene
{ {
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("change direction to down", () => changeDirectionTo(ManiaScrollingDirection.Down));
AddStep("change direction to up", () => changeDirectionTo(ManiaScrollingDirection.Up));
}
private void changeDirectionTo(ManiaScrollingDirection direction)
{
var rulesetConfig = (ManiaRulesetConfigManager)RulesetConfigs.GetConfigFor(new ManiaRuleset()).AsNonNull();
rulesetConfig.SetValue(ManiaRulesetSetting.ScrollDirection, direction);
}
} }
} }

View File

@ -43,8 +43,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
{ {
largeFaint = new Container largeFaint = new Container
{ {
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Height = ArgonNotePiece.NOTE_ACCENT_RATIO, Height = ArgonNotePiece.NOTE_ACCENT_RATIO,
Masking = true, Masking = true,
@ -81,11 +79,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
if (direction.NewValue == ScrollingDirection.Up) if (direction.NewValue == ScrollingDirection.Up)
{ {
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
largeFaint.Anchor = Anchor.TopCentre;
largeFaint.Origin = Anchor.TopCentre;
Y = ArgonNotePiece.NOTE_HEIGHT / 2; Y = ArgonNotePiece.NOTE_HEIGHT / 2;
} }
else else
{ {
Anchor = Anchor.BottomCentre; Anchor = Anchor.BottomCentre;
largeFaint.Anchor = Anchor.BottomCentre;
largeFaint.Origin = Anchor.BottomCentre;
Y = -ArgonNotePiece.NOTE_HEIGHT / 2; Y = -ArgonNotePiece.NOTE_HEIGHT / 2;
} }
} }

View File

@ -1,17 +0,0 @@
// 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.
#nullable disable
using Foundation;
using osu.Framework.iOS;
using osu.Game.Tests;
namespace osu.Game.Rulesets.Osu.Tests.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameAppDelegate
{
protected override Framework.Game CreateGame() => new OsuTestBrowser();
}
}

View File

@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using osu.Framework.iOS;
using osu.Game.Tests;
using UIKit;
namespace osu.Game.Rulesets.Osu.Tests.iOS namespace osu.Game.Rulesets.Osu.Tests.iOS
{ {
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, typeof(AppDelegate)); GameApplication.Main(new OsuTestBrowser());
} }
} }
} }

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
protected override bool AlwaysShowWhenSelected => true; protected override bool AlwaysShowWhenSelected => true;
protected override bool ShouldBeAlive => base.ShouldBeAlive protected override bool ShouldBeAlive => base.ShouldBeAlive
|| (ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION); || (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
protected OsuSelectionBlueprint(T hitObject) protected OsuSelectionBlueprint(T hitObject)
: base(hitObject) : base(hitObject)

View File

@ -252,13 +252,14 @@ namespace osu.Game.Rulesets.Osu.Skinning
renderer.SetBlend(BlendingParameters.Additive); renderer.SetBlend(BlendingParameters.Additive);
renderer.PushLocalMatrix(DrawInfo.Matrix); renderer.PushLocalMatrix(DrawInfo.Matrix);
TextureShader.Bind(); BindTextureShader(renderer);
texture.Bind(); texture.Bind();
for (int i = 0; i < points.Count; i++) for (int i = 0; i < points.Count; i++)
drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex); drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex);
TextureShader.Unbind(); UnbindTextureShader(renderer);
renderer.PopLocalMatrix(); renderer.PopLocalMatrix();
} }

View File

@ -1,17 +0,0 @@
// 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.
#nullable disable
using Foundation;
using osu.Framework.iOS;
using osu.Game.Tests;
namespace osu.Game.Rulesets.Taiko.Tests.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameAppDelegate
{
protected override Framework.Game CreateGame() => new OsuTestBrowser();
}
}

View File

@ -1,9 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using osu.Framework.iOS;
using osu.Game.Tests;
using UIKit;
namespace osu.Game.Rulesets.Taiko.Tests.iOS namespace osu.Game.Rulesets.Taiko.Tests.iOS
{ {
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, typeof(AppDelegate)); GameApplication.Main(new OsuTestBrowser());
} }
} }
} }

View File

@ -1,16 +0,0 @@
// 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.
#nullable disable
using Foundation;
using osu.Framework.iOS;
namespace osu.Game.Tests.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameAppDelegate
{
protected override Framework.Game CreateGame() => new OsuTestBrowser();
}
}

View File

@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using osu.Framework.iOS;
using UIKit;
namespace osu.Game.Tests.iOS namespace osu.Game.Tests.iOS
{ {
@ -11,7 +9,7 @@ namespace osu.Game.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, typeof(AppDelegate)); GameApplication.Main(new OsuTestBrowser());
} }
} }
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
@ -51,9 +49,11 @@ namespace osu.Game.Tests.Testing
[Test] [Test]
public void TestRetrieveShader() public void TestRetrieveShader()
{ {
AddAssert("ruleset shaders retrieved", () => AddStep("ruleset shaders retrieved without error", () =>
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestVertex.vs") != null && {
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestFragment.fs") != null); Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestVertex.vs");
Dependencies.Get<ShaderManager>().LoadRaw(@"sh_TestFragment.fs");
});
} }
[Test] [Test]
@ -76,12 +76,12 @@ namespace osu.Game.Tests.Testing
} }
public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources"); public override IResourceStore<byte[]> CreateResourceStore() => new NamespacedResourceStore<byte[]>(TestResources.GetStore(), @"Resources");
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new TestRulesetConfigManager(); public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new TestRulesetConfigManager();
public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>(); public override IEnumerable<Mod> GetModsFor(ModType type) => Array.Empty<Mod>();
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => null; public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod>? mods = null) => null!;
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null; public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => null!;
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null; public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => null!;
} }
private class TestRulesetConfigManager : IRulesetConfigManager private class TestRulesetConfigManager : IRulesetConfigManager

View File

@ -100,8 +100,10 @@ namespace osu.Game.Tests.Visual.Background
private IUniformBuffer<TriangleBorderData>? borderDataBuffer; private IUniformBuffer<TriangleBorderData>? borderDataBuffer;
public override void Draw(IRenderer renderer) protected override void BindUniformResources(IShader shader, IRenderer renderer)
{ {
base.BindUniformResources(shader, renderer);
borderDataBuffer ??= renderer.CreateUniformBuffer<TriangleBorderData>(); borderDataBuffer ??= renderer.CreateUniformBuffer<TriangleBorderData>();
borderDataBuffer.Data = borderDataBuffer.Data with borderDataBuffer.Data = borderDataBuffer.Data with
{ {
@ -109,9 +111,7 @@ namespace osu.Game.Tests.Visual.Background
TexelSize = texelSize TexelSize = texelSize
}; };
TextureShader.BindUniformBlock("m_BorderData", borderDataBuffer); shader.BindUniformBlock("m_BorderData", borderDataBuffer);
base.Draw(renderer);
} }
protected override bool CanDrawOpaqueInterior => false; protected override bool CanDrawOpaqueInterior => false;

View File

@ -13,6 +13,7 @@ using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
@ -106,6 +107,26 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible); AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible);
} }
[Test]
public void TestSaveFailedReplayWithStoryboardEndedDoesNotProgress()
{
CreateTest(() =>
{
AddStep("fail on first judgement", () => currentFailConditions = (_, _) => true);
AddStep("set storyboard duration to 0s", () => currentStoryboardDuration = 0);
});
AddUntilStep("storyboard ends", () => Player.GameplayClockContainer.CurrentTime >= currentStoryboardDuration);
AddUntilStep("wait for fail", () => Player.GameplayState.HasFailed);
AddUntilStep("wait for fail overlay", () => Player.FailOverlay.State.Value == Visibility.Visible);
AddUntilStep("wait for button clickable", () => Player.ChildrenOfType<SaveFailedScoreButton>().First().ChildrenOfType<OsuClickableContainer>().First().Enabled.Value);
AddStep("click save button", () => Player.ChildrenOfType<SaveFailedScoreButton>().First().ChildrenOfType<OsuClickableContainer>().First().TriggerClick());
// Test a regression where importing the fail replay would cause progression to results screen in a failed state.
AddWaitStep("wait some", 10);
AddAssert("player is still current screen", () => Player.IsCurrentScreen());
}
[Test] [Test]
public void TestShowResultsFalse() public void TestShowResultsFalse()
{ {

View File

@ -126,6 +126,7 @@ namespace osu.Game.Tests.Visual.Online
Id = 13926, Id = 13926,
TournamentId = 35, TournamentId = 35,
ImageLowRes = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US.jpg", ImageLowRes = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US.jpg",
Image = "https://assets.ppy.sh/tournament-banners/official/owc2022/profile/winner_US@2x.jpg",
}, },
Badges = new[] Badges = new[]
{ {

View File

@ -332,13 +332,6 @@ namespace osu.Game.Tournament
private void saveChanges() private void saveChanges()
{ {
foreach (var r in ladder.Rounds)
r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList();
ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat(
ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true)))
.ToList();
// Serialise before opening stream for writing, so if there's a failure it will leave the file in the previous state. // Serialise before opening stream for writing, so if there's a failure it will leave the file in the previous state.
string serialisedLadder = GetSerialisedLadder(); string serialisedLadder = GetSerialisedLadder();
@ -349,6 +342,13 @@ namespace osu.Game.Tournament
public string GetSerialisedLadder() public string GetSerialisedLadder()
{ {
foreach (var r in ladder.Rounds)
r.Matches = ladder.Matches.Where(p => p.Round.Value == r).Select(p => p.ID).ToList();
ladder.Progressions = ladder.Matches.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat(
ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true)))
.ToList();
return JsonConvert.SerializeObject(ladder, return JsonConvert.SerializeObject(ladder,
new JsonSerializerSettings new JsonSerializerSettings
{ {

View File

@ -306,7 +306,7 @@ namespace osu.Game.Graphics.Backgrounds
}; };
shader.Bind(); shader.Bind();
shader.BindUniformBlock("m_BorderData", borderDataBuffer); shader.BindUniformBlock(@"m_BorderData", borderDataBuffer);
foreach (TriangleParticle particle in parts) foreach (TriangleParticle particle in parts)
{ {

View File

@ -249,7 +249,7 @@ namespace osu.Game.Graphics.Backgrounds
}; };
shader.Bind(); shader.Bind();
shader.BindUniformBlock("m_BorderData", borderDataBuffer); shader.BindUniformBlock(@"m_BorderData", borderDataBuffer);
Vector2 relativeSize = Vector2.Divide(triangleSize, size); Vector2 relativeSize = Vector2.Divide(triangleSize, size);

View File

@ -76,17 +76,24 @@ namespace osu.Game.Graphics.Sprites
private IUniformBuffer<AnimationData> animationDataBuffer; private IUniformBuffer<AnimationData> animationDataBuffer;
private IVertexBatch<LogoAnimationVertex> animationVertexBatch; private IVertexBatch<LogoAnimationVertex> animationVertexBatch;
protected override void Blit(IRenderer renderer) protected override void BindUniformResources(IShader shader, IRenderer renderer)
{ {
if (DrawRectangle.Width == 0 || DrawRectangle.Height == 0) base.BindUniformResources(shader, renderer);
return;
animationDataBuffer ??= renderer.CreateUniformBuffer<AnimationData>(); animationDataBuffer ??= renderer.CreateUniformBuffer<AnimationData>();
animationVertexBatch ??= renderer.CreateQuadBatch<LogoAnimationVertex>(1, 2); animationVertexBatch ??= renderer.CreateQuadBatch<LogoAnimationVertex>(1, 2);
animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress }; animationDataBuffer.Data = animationDataBuffer.Data with { Progress = progress };
TextureShader.BindUniformBlock("m_AnimationData", animationDataBuffer); shader.BindUniformBlock(@"m_AnimationData", animationDataBuffer);
}
protected override void Blit(IRenderer renderer)
{
if (DrawRectangle.Width == 0 || DrawRectangle.Height == 0)
return;
base.Blit(renderer);
renderer.DrawQuad( renderer.DrawQuad(
Texture, Texture,

View File

@ -19,6 +19,11 @@ namespace osu.Game.Localisation
/// </summary> /// </summary>
public static LocalisableString RendererHeader => new TranslatableString(getKey(@"renderer_header"), @"Renderer"); public static LocalisableString RendererHeader => new TranslatableString(getKey(@"renderer_header"), @"Renderer");
/// <summary>
/// "Renderer"
/// </summary>
public static LocalisableString Renderer => new TranslatableString(getKey(@"renderer"), @"Renderer");
/// <summary> /// <summary>
/// "Frame limiter" /// "Frame limiter"
/// </summary> /// </summary>
@ -144,6 +149,12 @@ namespace osu.Game.Localisation
/// </summary> /// </summary>
public static LocalisableString Png => new TranslatableString(getKey(@"png_lossless"), @"PNG (lossless)"); public static LocalisableString Png => new TranslatableString(getKey(@"png_lossless"), @"PNG (lossless)");
/// <summary>
/// "In order to change the renderer, the game will close. Please open it again."
/// </summary>
public static LocalisableString ChangeRendererConfirmation =>
new TranslatableString(getKey(@"change_renderer_configuration"), @"In order to change the renderer, the game will close. Please open it again.");
private static string getKey(string key) => $"{prefix}:{key}"; private static string getKey(string key) => $"{prefix}:{key}";
} }
} }

View File

@ -50,16 +50,18 @@ namespace osu.Game.Localisation
public static LocalisableString NoAutoplayMod => new TranslatableString(getKey(@"no_autoplay_mod"), @"The current ruleset doesn't have an autoplay mod available!"); public static LocalisableString NoAutoplayMod => new TranslatableString(getKey(@"no_autoplay_mod"), @"The current ruleset doesn't have an autoplay mod available!");
/// <summary> /// <summary>
/// "osu! doesn&#39;t seem to be able to play audio correctly.\n\nPlease try changing your audio device to a working setting." /// "osu! doesn&#39;t seem to be able to play audio correctly.
///
/// Please try changing your audio device to a working setting."
/// </summary> /// </summary>
public static LocalisableString AudioPlaybackIssue => new TranslatableString(getKey(@"audio_playback_issue"), public static LocalisableString AudioPlaybackIssue => new TranslatableString(getKey(@"audio_playback_issue"), @"osu! doesn't seem to be able to play audio correctly.
@"osu! doesn't seem to be able to play audio correctly.\n\nPlease try changing your audio device to a working setting.");
Please try changing your audio device to a working setting.");
/// <summary> /// <summary>
/// "The score overlay is currently disabled. You can toggle this by pressing {0}." /// "The score overlay is currently disabled. You can toggle this by pressing {0}."
/// </summary> /// </summary>
public static LocalisableString ScoreOverlayDisabled(LocalisableString arg0) => new TranslatableString(getKey(@"score_overlay_disabled"), public static LocalisableString ScoreOverlayDisabled(LocalisableString arg0) => new TranslatableString(getKey(@"score_overlay_disabled"), @"The score overlay is currently disabled. You can toggle this by pressing {0}.", arg0);
@"The score overlay is currently disabled. You can toggle this by pressing {0}.", arg0);
private static string getKey(string key) => $@"{prefix}:{key}"; private static string getKey(string key) => $@"{prefix}:{key}";
} }

View File

@ -65,6 +65,11 @@ namespace osu.Game.Localisation
if (manager == null) if (manager == null)
return null; return null;
// When using the English culture, prefer the fallbacks rather than osu-resources baked strings.
// They are guaranteed to be up-to-date, and is also what a developer expects to see when making changes to `xxxStrings.cs` files.
if (EffectiveCulture.Name == @"en")
return null;
try try
{ {
return manager.GetString(key, EffectiveCulture); return manager.GetString(key, EffectiveCulture);

View File

@ -209,7 +209,7 @@ namespace osu.Game.Overlays.AccountCreation
if (!string.IsNullOrEmpty(errors.Message)) if (!string.IsNullOrEmpty(errors.Message))
passwordDescription.AddErrors(new[] { errors.Message }); passwordDescription.AddErrors(new[] { errors.Message });
game.OpenUrlExternally($"{errors.Redirect}?username={usernameTextBox.Text}&email={emailTextBox.Text}"); game.OpenUrlExternally($"{errors.Redirect}?username={usernameTextBox.Text}&email={emailTextBox.Text}", true);
} }
} }
else else

View File

@ -132,11 +132,10 @@ namespace osu.Game.Overlays.Comments
}, },
sideNumber = new OsuSpriteText sideNumber = new OsuSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.TopCentre,
Origin = Anchor.CentreRight, Origin = Anchor.BottomCentre,
Text = "+1", Text = "+1",
Font = OsuFont.GetFont(size: 14), Font = OsuFont.GetFont(size: 14),
Margin = new MarginPadding { Right = 3 },
Alpha = 0, Alpha = 0,
}, },
votesCounter = new OsuSpriteText votesCounter = new OsuSpriteText
@ -189,7 +188,7 @@ namespace osu.Game.Overlays.Comments
else else
sideNumber.FadeTo(IsHovered ? 1 : 0); sideNumber.FadeTo(IsHovered ? 1 : 0);
borderContainer.BorderThickness = IsHovered ? 3 : 0; borderContainer.BorderThickness = IsHovered ? 2 : 0;
} }
private void onHoverAction() private void onHoverAction()

View File

@ -55,7 +55,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
set => valueText.Text = value.ToLocalisableString("N0"); set => valueText.Text = value.ToLocalisableString("N0");
} }
public CountSection(LocalisableString header) protected CountSection(LocalisableString header)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y; AutoSizeAxes = Axes.Y;

View File

@ -32,7 +32,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
private FillFlowContainer<SettingsSlider<float>> scalingSettings = null!; private FillFlowContainer<SettingsSlider<float>> scalingSettings = null!;
private readonly Bindable<Display> currentDisplay = new Bindable<Display>(); private readonly Bindable<Display> currentDisplay = new Bindable<Display>();
private readonly IBindableList<WindowMode> windowModes = new BindableList<WindowMode>();
private Bindable<ScalingMode> scalingMode = null!; private Bindable<ScalingMode> scalingMode = null!;
private Bindable<Size> sizeFullscreen = null!; private Bindable<Size> sizeFullscreen = null!;
@ -75,7 +74,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
if (window != null) if (window != null)
{ {
currentDisplay.BindTo(window.CurrentDisplayBindable); currentDisplay.BindTo(window.CurrentDisplayBindable);
windowModes.BindTo(window.SupportedWindowModes);
window.DisplaysChanged += onDisplaysChanged; window.DisplaysChanged += onDisplaysChanged;
} }
@ -87,7 +85,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
windowModeDropdown = new SettingsDropdown<WindowMode> windowModeDropdown = new SettingsDropdown<WindowMode>
{ {
LabelText = GraphicsSettingsStrings.ScreenMode, LabelText = GraphicsSettingsStrings.ScreenMode,
ItemSource = windowModes, Items = window?.SupportedWindowModes,
CanBeShown = { Value = window?.SupportedWindowModes.Count() > 1 },
Current = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode), Current = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode),
}, },
displayDropdown = new DisplaySettingsDropdown displayDropdown = new DisplaySettingsDropdown
@ -181,8 +180,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
updateScreenModeWarning(); updateScreenModeWarning();
}, true); }, true);
windowModes.BindCollectionChanged((_, _) => updateDisplaySettingsVisibility());
currentDisplay.BindValueChanged(display => Schedule(() => currentDisplay.BindValueChanged(display => Schedule(() =>
{ {
resolutions.RemoveRange(1, resolutions.Count - 1); resolutions.RemoveRange(1, resolutions.Count - 1);
@ -236,7 +233,6 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
private void updateDisplaySettingsVisibility() private void updateDisplaySettingsVisibility()
{ {
windowModeDropdown.CanBeShown.Value = windowModes.Count > 1;
resolutionDropdown.CanBeShown.Value = resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen; resolutionDropdown.CanBeShown.Value = resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen;
displayDropdown.CanBeShown.Value = displayDropdown.Items.Count() > 1; displayDropdown.CanBeShown.Value = displayDropdown.Items.Count() > 1;
safeAreaConsiderationsCheckbox.CanBeShown.Value = host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero; safeAreaConsiderationsCheckbox.CanBeShown.Value = host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero;

View File

@ -1,15 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using System.Linq;
using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Overlays.Settings.Sections.Graphics namespace osu.Game.Overlays.Settings.Sections.Graphics
{ {
@ -17,12 +20,25 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{ {
protected override LocalisableString Header => GraphicsSettingsStrings.RendererHeader; protected override LocalisableString Header => GraphicsSettingsStrings.RendererHeader;
private bool automaticRendererInUse;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, OsuConfigManager osuConfig) private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, IDialogOverlay? dialogOverlay, OsuGame? game, GameHost host)
{ {
// NOTE: Compatability mode omitted var renderer = config.GetBindable<RendererType>(FrameworkSetting.Renderer);
automaticRendererInUse = renderer.Value == RendererType.Automatic;
SettingsEnumDropdown<RendererType> rendererDropdown;
Children = new Drawable[] Children = new Drawable[]
{ {
rendererDropdown = new RendererSettingsDropdown
{
LabelText = GraphicsSettingsStrings.Renderer,
Current = renderer,
Items = host.GetPreferredRenderersForCurrentPlatform().OrderBy(t => t).Where(t => t != RendererType.Vulkan),
Keywords = new[] { @"compatibility", @"directx" },
},
// TODO: this needs to be a custom dropdown at some point // TODO: this needs to be a custom dropdown at some point
new SettingsEnumDropdown<FrameSync> new SettingsEnumDropdown<FrameSync>
{ {
@ -41,6 +57,55 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
Current = osuConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay) Current = osuConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay)
}, },
}; };
renderer.BindValueChanged(r =>
{
if (r.NewValue == host.ResolvedRenderer)
return;
// Need to check startup renderer for the "automatic" case, as ResolvedRenderer above will track the final resolved renderer instead.
if (r.NewValue == RendererType.Automatic && automaticRendererInUse)
return;
dialogOverlay?.Push(new ConfirmDialog(GraphicsSettingsStrings.ChangeRendererConfirmation, () => game?.AttemptExit(), () =>
{
renderer.Value = automaticRendererInUse ? RendererType.Automatic : host.ResolvedRenderer;
}));
});
// TODO: remove this once we support SDL+android.
if (RuntimeInfo.OS == RuntimeInfo.Platform.Android)
{
rendererDropdown.Items = new[] { RendererType.Automatic, RendererType.OpenGLLegacy };
rendererDropdown.SetNoticeText("New renderer support for android is coming soon!", true);
}
}
private partial class RendererSettingsDropdown : SettingsEnumDropdown<RendererType>
{
protected override OsuDropdown<RendererType> CreateDropdown() => new RendererDropdown();
protected partial class RendererDropdown : DropdownControl
{
private RendererType hostResolvedRenderer;
private bool automaticRendererInUse;
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, GameHost host)
{
var renderer = config.GetBindable<RendererType>(FrameworkSetting.Renderer);
automaticRendererInUse = renderer.Value == RendererType.Automatic;
hostResolvedRenderer = host.ResolvedRenderer;
}
protected override LocalisableString GenerateItemText(RendererType item)
{
if (item == RendererType.Automatic && automaticRendererInUse)
return LocalisableString.Interpolate($"{base.GenerateItemText(item)} ({hostResolvedRenderer.GetDescription()})");
return base.GenerateItemText(item);
}
}
} }
} }
} }

View File

@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Mods
}; };
shader.Bind(); shader.Bind();
shader.BindUniformBlock("m_FlashlightParameters", flashlightParametersBuffer); shader.BindUniformBlock(@"m_FlashlightParameters", flashlightParametersBuffer);
renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction); renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction);

View File

@ -25,21 +25,28 @@ namespace osu.Game.Rulesets.UI
/// <summary> /// <summary>
/// The texture store to be used for the ruleset. /// The texture store to be used for the ruleset.
/// </summary> /// </summary>
/// <remarks>
/// Reads textures from the "Textures" folder in ruleset resources.
/// If not available locally, lookups will fallback to the global texture store.
/// </remarks>
public TextureStore TextureStore { get; } public TextureStore TextureStore { get; }
/// <summary> /// <summary>
/// The sample store to be used for the ruleset. /// The sample store to be used for the ruleset.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This is the local sample store pointing to the ruleset sample resources, /// Reads samples from the "Samples" folder in ruleset resources.
/// the cached sample store (<see cref="FallbackSampleStore"/>) retrieves from /// If not available locally, lookups will fallback to the global sample store.
/// this store and falls back to the parent store if this store doesn't have the requested sample.
/// </remarks> /// </remarks>
public ISampleStore SampleStore { get; } public ISampleStore SampleStore { get; }
/// <summary> /// <summary>
/// The shader manager to be used for the ruleset. /// The shader manager to be used for the ruleset.
/// </summary> /// </summary>
/// <remarks>
/// Reads shaders from the "Shaders" folder in ruleset resources.
/// If not available locally, lookups will fallback to the global shader manager.
/// </remarks>
public ShaderManager ShaderManager { get; } public ShaderManager ShaderManager { get; }
/// <summary> /// <summary>
@ -61,8 +68,7 @@ namespace osu.Game.Rulesets.UI
SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY; SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>())); CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>()));
ShaderManager = new ShaderManager(host.Renderer, new NamespacedResourceStore<byte[]>(resources, @"Shaders")); CacheAs(ShaderManager = new RulesetShaderManager(host.Renderer, new NamespacedResourceStore<byte[]>(resources, @"Shaders"), parent.Get<ShaderManager>()));
CacheAs(ShaderManager = new FallbackShaderManager(host.Renderer, ShaderManager, parent.Get<ShaderManager>()));
RulesetConfigManager = parent.Get<IRulesetConfigCache>().GetConfigFor(ruleset); RulesetConfigManager = parent.Get<IRulesetConfigCache>().GetConfigFor(ruleset);
if (RulesetConfigManager != null) if (RulesetConfigManager != null)
@ -190,24 +196,27 @@ namespace osu.Game.Rulesets.UI
} }
} }
private class FallbackShaderManager : ShaderManager private class RulesetShaderManager : ShaderManager
{ {
private readonly ShaderManager primary; private readonly ShaderManager parent;
private readonly ShaderManager fallback;
public FallbackShaderManager(IRenderer renderer, ShaderManager primary, ShaderManager fallback) public RulesetShaderManager(IRenderer renderer, NamespacedResourceStore<byte[]> rulesetResources, ShaderManager parent)
: base(renderer, new ResourceStore<byte[]>()) : base(renderer, rulesetResources)
{ {
this.primary = primary; this.parent = parent;
this.fallback = fallback;
} }
public override byte[]? LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name); public override IShader Load(string vertex, string fragment)
protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); try
if (primary.IsNotNull()) primary.Dispose(); {
return base.Load(vertex, fragment);
}
catch
{
// Shader lookup is very non-standard. Rather than returning null on missing shaders, exceptions are thrown.
return parent.Load(vertex, fragment);
}
} }
} }
} }

View File

@ -130,6 +130,8 @@ namespace osu.Game.Screens
loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, "TriangleBorder"));
loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE));
} }

View File

@ -73,11 +73,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private OsuSpriteText typeLabel = null!; private OsuSpriteText typeLabel = null!;
private LoadingLayer loadingLayer = null!; private LoadingLayer loadingLayer = null!;
public void SelectBeatmap() public void SelectBeatmap() => selectBeatmapButton.TriggerClick();
{
if (matchSubScreen.IsCurrentScreen())
matchSubScreen.Push(new MultiplayerMatchSongSelect(matchSubScreen.Room));
}
[Resolved] [Resolved]
private MultiplayerMatchSubScreen matchSubScreen { get; set; } = null!; private MultiplayerMatchSubScreen matchSubScreen { get; set; } = null!;
@ -97,6 +93,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
private IDisposable? applyingSettingsOperation; private IDisposable? applyingSettingsOperation;
private Drawable playlistContainer = null!; private Drawable playlistContainer = null!;
private DrawableRoomPlaylist drawablePlaylist = null!; private DrawableRoomPlaylist drawablePlaylist = null!;
private RoundedButton selectBeatmapButton = null!;
public MatchSettings(Room room) public MatchSettings(Room room)
{ {
@ -275,12 +272,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = DrawableRoomPlaylistItem.HEIGHT Height = DrawableRoomPlaylistItem.HEIGHT
}, },
new RoundedButton selectBeatmapButton = new RoundedButton
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 40, Height = 40,
Text = "Select beatmap", Text = "Select beatmap",
Action = SelectBeatmap Action = () =>
{
if (matchSubScreen.IsCurrentScreen())
matchSubScreen.Push(new MultiplayerMatchSongSelect(matchSubScreen.Room));
}
} }
} }
} }

View File

@ -358,14 +358,10 @@ namespace osu.Game.Screens.Play
ScoreProcessor.RevertResult(r); ScoreProcessor.RevertResult(r);
}; };
DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded => DimmableStoryboard.HasStoryboardEnded.ValueChanged += _ => checkScoreCompleted();
{
if (storyboardEnded.NewValue)
progressToResults(true);
};
// Bind the judgement processors to ourselves // Bind the judgement processors to ourselves
ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged); ScoreProcessor.HasCompleted.BindValueChanged(_ => checkScoreCompleted());
HealthProcessor.Failed += onFail; HealthProcessor.Failed += onFail;
// Provide judgement processors to mods after they're loaded so that they're on the gameplay clock, // Provide judgement processors to mods after they're loaded so that they're on the gameplay clock,
@ -706,19 +702,20 @@ namespace osu.Game.Screens.Play
/// <summary> /// <summary>
/// Handles changes in player state which may progress the completion of gameplay / this screen's lifetime. /// Handles changes in player state which may progress the completion of gameplay / this screen's lifetime.
/// </summary> /// </summary>
/// <exception cref="InvalidOperationException">Thrown if this method is called more than once without changing state.</exception> private void checkScoreCompleted()
private void scoreCompletionChanged(ValueChangedEvent<bool> completed)
{ {
// If this player instance is in the middle of an exit, don't attempt any kind of state update. // If this player instance is in the middle of an exit, don't attempt any kind of state update.
if (!this.IsCurrentScreen()) if (!this.IsCurrentScreen())
return; return;
// Special case to handle rewinding post-completion. This is the only way already queued forward progress can be cancelled. // Handle cases of arriving at this method when not in a completed state.
// TODO: Investigate whether this can be moved to a RewindablePlayer subclass or similar. // - When a storyboard completion triggered this call earlier than gameplay finishes.
// Currently, even if this scenario is hit, prepareScoreForDisplay has already been queued (and potentially run). // - When a replay has been rewound before a queued resultsDisplayDelegate has run.
// In scenarios where rewinding is possible (replay, spectating) this is a non-issue as no submission/import work is done, //
// but it still doesn't feel right that this exists here. // Currently, even if this scenario is hit, prepareAndImportScoreAsync has already been queued (and potentially run).
if (!completed.NewValue) // In the scenarios above, this is a non-issue, but it still feels a bit convoluted to have to cancel in this method.
// Maybe this can be improved with further refactoring.
if (!ScoreProcessor.HasCompleted.Value)
{ {
resultsDisplayDelegate?.Cancel(); resultsDisplayDelegate?.Cancel();
resultsDisplayDelegate = null; resultsDisplayDelegate = null;
@ -742,12 +739,12 @@ namespace osu.Game.Screens.Play
if (!Configuration.ShowResults) if (!Configuration.ShowResults)
return; return;
bool storyboardHasOutro = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value; bool storyboardStillRunning = DimmableStoryboard.ContentDisplayed && !DimmableStoryboard.HasStoryboardEnded.Value;
if (storyboardHasOutro) // If the current beatmap has a storyboard, this method will be called again on storyboard completion.
// Alternatively, the user may press the outro skip button, forcing immediate display of the results screen.
if (storyboardStillRunning)
{ {
// if the current beatmap has a storyboard, the progression to results will be handled by the storyboard ending
// or the user pressing the skip outro button.
skipOutroOverlay.Show(); skipOutroOverlay.Show();
return; return;
} }
@ -793,6 +790,8 @@ namespace osu.Game.Screens.Play
// This player instance may already be in the process of exiting. // This player instance may already be in the process of exiting.
return; return;
Debug.Assert(ScoreProcessor.Rank.Value != ScoreRank.F);
this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely()));
}, Time.Current + delay, 50); }, Time.Current + delay, 50);

View File

@ -257,6 +257,7 @@ namespace osu.Game.Screens.Select
public FilterControlTextBox() public FilterControlTextBox()
{ {
Height += filter_text_size; Height += filter_text_size;
TextContainer.Height *= (Height - filter_text_size) / Height;
TextContainer.Margin = new MarginPadding { Bottom = filter_text_size }; TextContainer.Margin = new MarginPadding { Bottom = filter_text_size };
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace osu.Game.Users namespace osu.Game.Users
@ -17,7 +16,7 @@ namespace osu.Game.Users
[JsonProperty("image")] [JsonProperty("image")]
public string ImageLowRes = null!; public string ImageLowRes = null!;
// TODO: remove when api returns @2x image link: https://github.com/ppy/osu-web/issues/9816 [JsonProperty("image@2x")]
public string Image => $@"{Path.ChangeExtension(ImageLowRes, null)}@2x{Path.GetExtension(ImageLowRes)}"; public string Image = null!;
} }
} }

View File

@ -36,8 +36,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="10.20.0" /> <PackageReference Include="Realm" Version="10.20.0" />
<PackageReference Include="ppy.osu.Framework" Version="2023.314.0" /> <PackageReference Include="ppy.osu.Framework" Version="2023.403.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.320.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2023.402.0" />
<PackageReference Include="Sentry" Version="3.28.1" /> <PackageReference Include="Sentry" Version="3.28.1" />
<PackageReference Include="SharpCompress" Version="0.32.2" /> <PackageReference Include="SharpCompress" Version="0.32.2" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />

View File

@ -16,6 +16,6 @@
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier> <RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.314.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2023.403.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,29 +0,0 @@
// 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.
#nullable disable
using System.Threading.Tasks;
using Foundation;
using osu.Framework.iOS;
using UIKit;
namespace osu.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameAppDelegate
{
private OsuGameIOS game;
protected override Framework.Game CreateGame() => game = new OsuGameIOS();
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
if (url.IsFileUrl)
Task.Run(() => game.Import(url.Path));
else
Task.Run(() => game.HandleLink(url.AbsoluteString));
return true;
}
}
}

View File

@ -1,9 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
#nullable disable using osu.Framework.iOS;
using UIKit;
namespace osu.iOS namespace osu.iOS
{ {
@ -11,7 +9,7 @@ namespace osu.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, typeof(AppDelegate)); GameApplication.Main(new OsuGameIOS());
} }
} }
} }

View File

@ -1,36 +0,0 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Configuration;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
namespace osu.iOS
{
public partial class IOSMouseSettings : SettingsSubsection
{
protected override LocalisableString Header => MouseSettingsStrings.Mouse;
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig)
{
Children = new Drawable[]
{
new SettingsCheckbox
{
LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel),
},
new SettingsCheckbox
{
LabelText = MouseSettingsStrings.DisableMouseButtons,
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
},
};
}
}
}

View File

@ -7,10 +7,7 @@ using System;
using Foundation; using Foundation;
using Microsoft.Maui.Devices; using Microsoft.Maui.Devices;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Handlers;
using osu.Framework.iOS.Input;
using osu.Game; using osu.Game;
using osu.Game.Overlays.Settings;
using osu.Game.Updater; using osu.Game.Updater;
using osu.Game.Utils; using osu.Game.Utils;
@ -29,18 +26,6 @@ namespace osu.iOS
// Because we have the home indicator (mostly) hidden we don't really care about drawing in this region. // Because we have the home indicator (mostly) hidden we don't really care about drawing in this region.
Edges.Bottom; Edges.Bottom;
public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
{
switch (handler)
{
case IOSMouseHandler:
return new IOSMouseSettings();
default:
return base.CreateSettingsSubsectionFor(handler);
}
}
private class IOSBatteryInfo : BatteryInfo private class IOSBatteryInfo : BatteryInfo
{ {
public override double? ChargeLevel => Battery.ChargeLevel; public override double? ChargeLevel => Battery.ChargeLevel;