diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 56b3ebe87b..798e54e155 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -132,8 +132,8 @@ jobs:
with:
dotnet-version: "6.0.x"
- # Contrary to seemingly any other msbuild, msbuild running on macOS/Mono
- # cannot accept .sln(f) files as arguments.
- # Build just the main game for now.
+ - name: Install .NET Workloads
+ run: dotnet workload install maui-ios
+
- name: Build
- run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug
+ run: dotnet build -c Debug osu.iOS
diff --git a/README.md b/README.md
index 75d61dad4d..0de82eba75 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ If you are looking to install or test osu! without setting up a development envi
**Latest build:**
-| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
+| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
| ------------- | ------------- | ------------- | ------------- | ------------- |
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
diff --git a/appveyor.yml b/appveyor.yml
index 5be73f9875..19ef6ae63f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,6 @@
clone_depth: 1
version: '{branch}-{build}'
-image: Visual Studio 2019
+image: Visual Studio 2022
cache:
- '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
@@ -11,6 +11,7 @@ dotnet_csproj:
before_build:
- cmd: dotnet --info # Useful when version mismatch between CI and local
+ - cmd: dotnet workload install maui-ios # Change to `dotnet workload restore` once there's no old projects
- cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
build:
diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs b/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs
index 71d943ece1..1fcb0aa427 100644
--- a/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs
+++ b/osu.Game.Rulesets.Catch.Tests.iOS/Application.cs
@@ -3,7 +3,6 @@
#nullable disable
-using osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Catch.Tests.iOS
@@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{
public static void Main(string[] args)
{
- UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist b/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist
index 16a2b99997..5ace6c07f5 100644
--- a/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist
+++ b/osu.Game.Rulesets.Catch.Tests.iOS/Info.plist
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.4
UIDeviceFamily
1
diff --git a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
index be6044bbd0..acf12bb0ac 100644
--- a/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Catch.Tests.iOS/osu.Game.Rulesets.Catch.Tests.iOS.csproj
@@ -1,35 +1,19 @@
-
-
+
- Debug
- iPhoneSimulator
- {4004C7B7-1A62-43F1-9DF2-52450FA67E70}
Exe
+ net6.0-ios
+ 13.4
osu.Game.Rulesets.Catch.Tests
osu.Game.Rulesets.Catch.Tests.iOS
-
-
-
- Linker.xml
-
-
-
%(RecursiveDir)%(Filename)%(Extension)
-
- {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
- osu.Game
-
-
- {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}
- osu.Game.Rulesets.Catch
-
+
+
-
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs b/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs
index 2d1015387a..a508198f7f 100644
--- a/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs
+++ b/osu.Game.Rulesets.Mania.Tests.iOS/Application.cs
@@ -3,7 +3,6 @@
#nullable disable
-using osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Mania.Tests.iOS
@@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{
public static void Main(string[] args)
{
- UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist b/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist
index 82d1c8ea24..ff5dde856e 100644
--- a/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist
+++ b/osu.Game.Rulesets.Mania.Tests.iOS/Info.plist
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.4
UIDeviceFamily
1
diff --git a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
index 88ad484bc1..51e07dd6c1 100644
--- a/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Mania.Tests.iOS/osu.Game.Rulesets.Mania.Tests.iOS.csproj
@@ -1,35 +1,19 @@
-
-
+
- Debug
- iPhoneSimulator
- {39FD990E-B6CE-4B2A-999F-BC008CF2C64C}
Exe
+ net6.0-ios
+ 13.4
osu.Game.Rulesets.Mania.Tests
osu.Game.Rulesets.Mania.Tests.iOS
-
-
-
- Linker.xml
-
-
-
%(RecursiveDir)%(Filename)%(Extension)
-
- {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
- osu.Game
-
-
- {48F4582B-7687-4621-9CBE-5C24197CB536}
- osu.Game.Rulesets.Mania
-
+
+
-
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs b/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs
index ad23f3ee33..6ef29fa68e 100644
--- a/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs
+++ b/osu.Game.Rulesets.Osu.Tests.iOS/Application.cs
@@ -3,7 +3,6 @@
#nullable disable
-using osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Osu.Tests.iOS
@@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{
public static void Main(string[] args)
{
- UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist b/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist
index a88b74695c..1e33f2ff16 100644
--- a/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist
+++ b/osu.Game.Rulesets.Osu.Tests.iOS/Info.plist
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.4
UIDeviceFamily
1
diff --git a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
index 545abcec6c..7d50deb8ba 100644
--- a/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Osu.Tests.iOS/osu.Game.Rulesets.Osu.Tests.iOS.csproj
@@ -1,35 +1,20 @@
-
-
+
- Debug
- iPhoneSimulator
- {6653CA6F-DB06-4604-A3FD-762E25C2AF96}
+ Exe
+ net6.0-ios
+ 13.4
Exe
osu.Game.Rulesets.Osu.Tests
osu.Game.Rulesets.Osu.Tests.iOS
-
-
-
- Linker.xml
-
-
-
%(RecursiveDir)%(Filename)%(Extension)
-
- {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
- osu.Game
-
-
- {C92A607B-1FDD-4954-9F92-03FF547D9080}
- osu.Game.Rulesets.Osu
-
+
+
-
-
\ No newline at end of file
+
diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs b/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs
index 1ebbd61a94..0e3a953728 100644
--- a/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs
+++ b/osu.Game.Rulesets.Taiko.Tests.iOS/Application.cs
@@ -3,7 +3,6 @@
#nullable disable
-using osu.Framework.iOS;
using UIKit;
namespace osu.Game.Rulesets.Taiko.Tests.iOS
@@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{
public static void Main(string[] args)
{
- UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist b/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist
index 9628475b3e..76cb3c0db0 100644
--- a/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist
+++ b/osu.Game.Rulesets.Taiko.Tests.iOS/Info.plist
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.4
UIDeviceFamily
1
diff --git a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
index 8ee640cd99..e648a11299 100644
--- a/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests.iOS/osu.Game.Rulesets.Taiko.Tests.iOS.csproj
@@ -1,35 +1,19 @@
-
-
+
- Debug
- iPhoneSimulator
- {7E408809-66AC-49D1-AF4D-98834F9B979A}
Exe
+ net6.0-ios
+ 13.4
osu.Game.Rulesets.Taiko.Tests
osu.Game.Rulesets.Taiko.Tests.iOS
-
-
-
- Linker.xml
-
-
-
%(RecursiveDir)%(Filename)%(Extension)
-
- {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
- osu.Game
-
-
- {F167E17A-7DE6-4AF5-B920-A5112296C695}
- osu.Game.Rulesets.Taiko
-
+
+
-
-
\ No newline at end of file
+
diff --git a/osu.Game.Tests.iOS/Application.cs b/osu.Game.Tests.iOS/Application.cs
index cf36fea139..4678be4fb8 100644
--- a/osu.Game.Tests.iOS/Application.cs
+++ b/osu.Game.Tests.iOS/Application.cs
@@ -3,7 +3,6 @@
#nullable disable
-using osu.Framework.iOS;
using UIKit;
namespace osu.Game.Tests.iOS
@@ -12,7 +11,7 @@ namespace osu.Game.Tests.iOS
{
public static void Main(string[] args)
{
- UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.Game.Tests.iOS/Info.plist b/osu.Game.Tests.iOS/Info.plist
index 31e2b3f257..ac661f6263 100644
--- a/osu.Game.Tests.iOS/Info.plist
+++ b/osu.Game.Tests.iOS/Info.plist
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.4
UIDeviceFamily
1
diff --git a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
index 05b3cad6da..79771fcd50 100644
--- a/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
+++ b/osu.Game.Tests.iOS/osu.Game.Tests.iOS.csproj
@@ -1,54 +1,26 @@
-
-
+
- Debug
- iPhoneSimulator
Exe
- {65FF8E19-6934-469B-B690-23C6D6E56A17}
+ net6.0-ios
+ 13.4
osu.Game.Tests
osu.Game.Tests.iOS
-
-
-
- Linker.xml
-
-
-
%(RecursiveDir)%(Filename)%(Extension)
-
- $(NoWarn);CA2007
-
-
- {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
- osu.Game
-
-
- {C92A607B-1FDD-4954-9F92-03FF547D9080}
- osu.Game.Rulesets.Osu
-
-
- {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}
- osu.Game.Rulesets.Catch
-
-
- {48F4582B-7687-4621-9CBE-5C24197CB536}
- osu.Game.Rulesets.Mania
-
-
- {F167E17A-7DE6-4AF5-B920-A5112296C695}
- osu.Game.Rulesets.Taiko
-
+
+
+
+
+
-
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
index 8f1eb98c79..ffd034e4d2 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
@@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestFinalFramesPurgedBeforeEndingPlay()
{
- AddStep("begin playing", () => spectatorClient.BeginPlaying(TestGameplayState.Create(new OsuRuleset()), new Score()));
+ AddStep("begin playing", () => spectatorClient.BeginPlaying(0, TestGameplayState.Create(new OsuRuleset()), new Score()));
AddStep("send frames and finish play", () =>
{
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
index 1ad1da0994..794860b9ec 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs
@@ -147,7 +147,7 @@ namespace osu.Game.Tests.Visual.Gameplay
}
};
- spectatorClient.BeginPlaying(TestGameplayState.Create(new OsuRuleset()), recordingScore);
+ spectatorClient.BeginPlaying(0, TestGameplayState.Create(new OsuRuleset()), recordingScore);
spectatorClient.OnNewFrames += onNewFrames;
});
}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonsInput.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonsInput.cs
new file mode 100644
index 0000000000..985f613b63
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonsInput.cs
@@ -0,0 +1,129 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Settings;
+using NUnit.Framework;
+using osuTK;
+using osu.Game.Overlays;
+using osu.Game.Graphics.UserInterfaceV2;
+using osu.Game.Graphics.UserInterface;
+using osu.Framework.Allocation;
+using osu.Game.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osuTK.Graphics;
+using osu.Game.Graphics.Sprites;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ public partial class TestSceneButtonsInput : OsuManualInputManagerTestScene
+ {
+ private const int width = 500;
+
+ [Cached]
+ private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
+
+ private readonly SettingsButton settingsButton;
+ private readonly OsuClickableContainer clickableContainer;
+ private readonly RoundedButton roundedButton;
+ private readonly ShearedButton shearedButton;
+
+ public TestSceneButtonsInput()
+ {
+ Add(new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Y,
+ Width = 500,
+ Spacing = new Vector2(0, 5),
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ clickableContainer = new OsuClickableContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ Height = 40,
+ Enabled = { Value = true },
+ Masking = true,
+ CornerRadius = 20,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Red
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Text = "Rounded clickable container"
+ }
+ }
+ },
+ settingsButton = new SettingsButton
+ {
+ Enabled = { Value = true },
+ Text = "Settings button"
+ },
+ roundedButton = new RoundedButton
+ {
+ RelativeSizeAxes = Axes.X,
+ Enabled = { Value = true },
+ Text = "Rounded button"
+ },
+ shearedButton = new ShearedButton(width)
+ {
+ Text = "Sheared button",
+ LighterColour = Colour4.FromHex("#FFFFFF"),
+ DarkerColour = Colour4.FromHex("#FFCC22"),
+ TextColour = Colour4.Black,
+ Height = 40,
+ Enabled = { Value = true },
+ Padding = new MarginPadding(0)
+ }
+ }
+ });
+ }
+
+ [Test]
+ public void TestSettingsButtonInput()
+ {
+ AddStep("Move cursor to button", () => InputManager.MoveMouseTo(settingsButton));
+ AddAssert("Button is hovered", () => settingsButton.IsHovered);
+ AddStep("Move cursor to padded area", () => InputManager.MoveMouseTo(settingsButton.ScreenSpaceDrawQuad.TopLeft + new Vector2(SettingsPanel.CONTENT_MARGINS / 2f, 10)));
+ AddAssert("Cursor within a button", () => settingsButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
+ AddAssert("Button is not hovered", () => !settingsButton.IsHovered);
+ }
+
+ [Test]
+ public void TestRoundedButtonInput()
+ {
+ AddStep("Move cursor to button", () => InputManager.MoveMouseTo(roundedButton));
+ AddAssert("Button is hovered", () => roundedButton.IsHovered);
+ AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(roundedButton.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
+ AddAssert("Cursor within a button", () => roundedButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
+ AddAssert("Button is not hovered", () => !roundedButton.IsHovered);
+ }
+
+ [Test]
+ public void TestShearedButtonInput()
+ {
+ AddStep("Move cursor to button", () => InputManager.MoveMouseTo(shearedButton));
+ AddAssert("Button is hovered", () => shearedButton.IsHovered);
+ AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(shearedButton.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
+ AddAssert("Cursor within a button", () => shearedButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
+ AddAssert("Button is not hovered", () => !shearedButton.IsHovered);
+ }
+
+ [Test]
+ public void TestRoundedClickableContainerInput()
+ {
+ AddStep("Move cursor to button", () => InputManager.MoveMouseTo(clickableContainer));
+ AddAssert("Button is hovered", () => clickableContainer.IsHovered);
+ AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(clickableContainer.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
+ AddAssert("Cursor within a button", () => clickableContainer.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
+ AddAssert("Button is not hovered", () => !clickableContainer.IsHovered);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuButton.cs
deleted file mode 100644
index 41e5d47093..0000000000
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuButton.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-#nullable disable
-
-using NUnit.Framework;
-using osu.Framework.Graphics;
-using osu.Game.Graphics.UserInterface;
-using osuTK;
-
-namespace osu.Game.Tests.Visual.UserInterface
-{
- public partial class TestSceneOsuButton : OsuTestScene
- {
- [Test]
- public void TestToggleEnabled()
- {
- OsuButton button = null;
-
- AddStep("add button", () => Child = button = new OsuButton
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(200),
- Text = "Button"
- });
-
- AddToggleStep("toggle enabled", toggle =>
- {
- for (int i = 0; i < 6; i++)
- button.Action = toggle ? () => { } : null;
- });
- }
-
- [Test]
- public void TestInitiallyDisabled()
- {
- AddStep("add button", () => Child = new OsuButton
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Size = new Vector2(200),
- Text = "Button"
- });
- }
- }
-}
diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs
index 4729ddf1a8..6ec393df4b 100644
--- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs
@@ -1,14 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterface;
+using osuTK;
namespace osu.Game.Graphics.Containers
{
@@ -18,6 +17,12 @@ namespace osu.Game.Graphics.Containers
private readonly Container content = new Container { RelativeSizeAxes = Axes.Both };
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
+ // base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation).
+ base.ReceivePositionalInputAt(screenSpacePos)
+ // Implementations often apply masking / edge rounding at a content level, so it's imperative to check that as well.
+ && Content.ReceivePositionalInputAt(screenSpacePos);
+
protected override Container Content => content;
protected virtual HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet) { Enabled = { BindTarget = Enabled } };
@@ -38,11 +43,8 @@ namespace osu.Game.Graphics.Containers
content.AutoSizeAxes = AutoSizeAxes;
}
- InternalChildren = new Drawable[]
- {
- content,
- CreateHoverSounds(sampleSet)
- };
+ AddInternal(content);
+ Add(CreateHoverSounds(sampleSet));
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index fa61b06cff..805dfcaa95 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -13,6 +11,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
+using osuTK;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterface
@@ -20,16 +19,12 @@ namespace osu.Game.Graphics.UserInterface
///
/// A button with added default sound effects.
///
- public partial class OsuButton : Button
+ public abstract partial class OsuButton : Button
{
public LocalisableString Text
{
- get => SpriteText?.Text ?? default;
- set
- {
- if (SpriteText != null)
- SpriteText.Text = value;
- }
+ get => SpriteText.Text;
+ set => SpriteText.Text = value;
}
private Color4? backgroundColour;
@@ -66,13 +61,19 @@ namespace osu.Game.Graphics.UserInterface
protected override Container Content { get; }
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
+ // base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation).
+ base.ReceivePositionalInputAt(screenSpacePos)
+ // Implementations often apply masking / edge rounding at a content level, so it's imperative to check that as well.
+ && Content.ReceivePositionalInputAt(screenSpacePos);
+
protected Box Hover;
protected Box Background;
protected SpriteText SpriteText;
private readonly Box flashLayer;
- public OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
+ protected OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
{
Height = 40;
@@ -115,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface
});
if (hoverSounds.HasValue)
- AddInternal(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
+ Add(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs
index ca6d2932f7..8fd79bd703 100644
--- a/osu.Game/Online/HubClientConnector.cs
+++ b/osu.Game/Online/HubClientConnector.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Net;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
@@ -59,17 +60,21 @@ namespace osu.Game.Online
var builder = new HubConnectionBuilder()
.WithUrl(endpoint, options =>
{
- // Use HttpClient.DefaultProxy once on net6 everywhere.
- // The credential setter can also be removed at this point.
- options.Proxy = WebRequest.DefaultWebProxy;
- if (options.Proxy != null)
- options.Proxy.Credentials = CredentialCache.DefaultCredentials;
+ // Configuring proxies is not supported on iOS, see https://github.com/xamarin/xamarin-macios/issues/14632.
+ if (RuntimeInfo.OS != RuntimeInfo.Platform.iOS)
+ {
+ // Use HttpClient.DefaultProxy once on net6 everywhere.
+ // The credential setter can also be removed at this point.
+ options.Proxy = WebRequest.DefaultWebProxy;
+ if (options.Proxy != null)
+ options.Proxy.Credentials = CredentialCache.DefaultCredentials;
+ }
options.Headers.Add("Authorization", $"Bearer {api.AccessToken}");
options.Headers.Add("OsuVersionHash", versionHash);
});
- if (RuntimeInfo.SupportsJIT && preferMessagePack)
+ if (RuntimeFeature.IsDynamicCodeCompiled && preferMessagePack)
{
builder.AddMessagePackProtocol(options =>
{
diff --git a/osu.Game/Online/Spectator/ISpectatorServer.cs b/osu.Game/Online/Spectator/ISpectatorServer.cs
index 25785f60a4..fa9d04792a 100644
--- a/osu.Game/Online/Spectator/ISpectatorServer.cs
+++ b/osu.Game/Online/Spectator/ISpectatorServer.cs
@@ -15,8 +15,9 @@ namespace osu.Game.Online.Spectator
///
/// Signal the start of a new play session.
///
+ /// The score submission token.
/// The state of gameplay.
- Task BeginPlaySession(SpectatorState state);
+ Task BeginPlaySession(long? scoreToken, SpectatorState state);
///
/// Send a bundle of frame data for the current play session.
diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
index d69bd81b57..d5c1e56761 100644
--- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
+++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Online.Spectator
}
}
- protected override async Task BeginPlayingInternal(SpectatorState state)
+ protected override async Task BeginPlayingInternal(long? scoreToken, SpectatorState state)
{
if (!IsConnected.Value)
return;
@@ -56,7 +56,7 @@ namespace osu.Game.Online.Spectator
try
{
- await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), state);
+ await connection.InvokeAsync(nameof(ISpectatorServer.BeginPlaySession), scoreToken, state);
}
catch (Exception exception)
{
@@ -65,7 +65,7 @@ namespace osu.Game.Online.Spectator
Debug.Assert(connector != null);
await connector.Reconnect();
- await BeginPlayingInternal(state);
+ await BeginPlayingInternal(scoreToken, state);
}
// Exceptions can occur if, for instance, the locally played beatmap doesn't have a server-side counterpart.
diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs
index 0a499a202c..fce61c019b 100644
--- a/osu.Game/Online/Spectator/SpectatorClient.cs
+++ b/osu.Game/Online/Spectator/SpectatorClient.cs
@@ -76,6 +76,7 @@ namespace osu.Game.Online.Spectator
private IBeatmap? currentBeatmap;
private Score? currentScore;
+ private long? currentScoreToken;
private readonly Queue pendingFrameBundles = new Queue();
@@ -108,7 +109,7 @@ namespace osu.Game.Online.Spectator
// re-send state in case it wasn't received
if (IsPlaying)
// TODO: this is likely sent out of order after a reconnect scenario. needs further consideration.
- BeginPlayingInternal(currentState);
+ BeginPlayingInternal(currentScoreToken, currentState);
}
else
{
@@ -159,7 +160,7 @@ namespace osu.Game.Online.Spectator
return Task.CompletedTask;
}
- public void BeginPlaying(GameplayState state, Score score)
+ public void BeginPlaying(long? scoreToken, GameplayState state, Score score)
{
// This schedule is only here to match the one below in `EndPlaying`.
Schedule(() =>
@@ -178,8 +179,9 @@ namespace osu.Game.Online.Spectator
currentBeatmap = state.Beatmap;
currentScore = score;
+ currentScoreToken = scoreToken;
- BeginPlayingInternal(currentState);
+ BeginPlayingInternal(currentScoreToken, currentState);
});
}
@@ -264,7 +266,7 @@ namespace osu.Game.Online.Spectator
});
}
- protected abstract Task BeginPlayingInternal(SpectatorState state);
+ protected abstract Task BeginPlayingInternal(long? scoreToken, SpectatorState state);
protected abstract Task SendFramesInternal(FrameDataBundle bundle);
diff --git a/osu.Game/Overlays/Chat/DrawableUsername.cs b/osu.Game/Overlays/Chat/DrawableUsername.cs
index 7026d519a5..6bae498a6c 100644
--- a/osu.Game/Overlays/Chat/DrawableUsername.cs
+++ b/osu.Game/Overlays/Chat/DrawableUsername.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Chat
public Color4 AccentColour { get; }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
- Child.ReceivePositionalInputAt(screenSpacePos);
+ colouredDrawable.ReceivePositionalInputAt(screenSpacePos);
public float FontSize
{
@@ -87,13 +87,13 @@ namespace osu.Game.Overlays.Chat
{
AccentColour = default_colours[user.Id % default_colours.Length];
- Child = colouredDrawable = drawableText;
+ Add(colouredDrawable = drawableText);
}
else
{
AccentColour = Color4Extensions.FromHex(user.Colour);
- Child = new Container
+ Add(new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Chat
}
}
}
- };
+ });
}
}
diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
index 5452e4b9eb..a78ae24da2 100644
--- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
+++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs
@@ -25,6 +25,11 @@ namespace osu.Game.Scoring.Legacy
/// Database version in stable-compatible YYYYMMDD format.
/// Should be incremented if any changes are made to the format/usage.
///
+ ///
+ ///
+ /// - 30000001: Appends to the end of scores.
+ ///
+ ///
public const int LATEST_VERSION = 30000001;
///
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs
index 5a686ffa72..930bea4497 100644
--- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorPlayer.cs
@@ -7,6 +7,7 @@ using osu.Framework.Audio;
using osu.Game.Beatmaps;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
+using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
{
@@ -70,5 +71,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
clockAdjustmentsFromMods.BindAdjustments(gameplayClockContainer.AdjustmentsFromMods);
return gameplayClockContainer;
}
+
+ protected override ResultsScreen CreateResults(ScoreInfo score) => new MultiSpectatorResultsScreen(score);
}
}
diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs
new file mode 100644
index 0000000000..fe3f02466d
--- /dev/null
+++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorResultsScreen.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+#nullable disable
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Online.API;
+using osu.Game.Scoring;
+using osu.Game.Screens.Play;
+
+namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
+{
+ public partial class MultiSpectatorResultsScreen : SpectatorResultsScreen
+ {
+ public MultiSpectatorResultsScreen(ScoreInfo score)
+ : base(score)
+ {
+ }
+
+ protected override APIRequest FetchScores(Action> scoresCallback) => null;
+
+ protected override APIRequest FetchNextPage(int direction, Action> scoresCallback) => null;
+ }
+}
diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs
index 14453c8cbe..4c507f8c67 100644
--- a/osu.Game/Screens/Play/SubmittingPlayer.cs
+++ b/osu.Game/Screens/Play/SubmittingPlayer.cs
@@ -148,7 +148,7 @@ namespace osu.Game.Screens.Play
realmBeatmap.LastPlayed = DateTimeOffset.Now;
});
- spectatorClient.BeginPlaying(GameplayState, Score);
+ spectatorClient.BeginPlaying(token, GameplayState, Score);
}
public override bool OnExiting(ScreenExitEvent e)
diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs
index a76f6c7052..1db35b3aaa 100644
--- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs
+++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs
@@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.Spectator
}
}
- protected override Task BeginPlayingInternal(SpectatorState state)
+ protected override Task BeginPlayingInternal(long? scoreToken, SpectatorState state)
{
// Track the local user's playing beatmap ID.
Debug.Assert(state.BeatmapID != null);
diff --git a/osu.iOS.props b/osu.iOS.props
index b2aa3caa14..9168c019ba 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -1,72 +1,19 @@
- 8.0
- {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- Resources
- PackageReference
- bin\$(Platform)\$(Configuration)
- cjk,mideast,other,rare,west
- false
- NSUrlSessionHandler
-
iPhone Developer
- true
-
-
-
- --nosymbolstrip=BASS_FX_BPM_BeatCallbackReset --nosymbolstrip=BASS_FX_BPM_BeatCallbackSet --nosymbolstrip=BASS_FX_BPM_BeatDecodeGet --nosymbolstrip=BASS_FX_BPM_BeatFree --nosymbolstrip=BASS_FX_BPM_BeatGetParameters --nosymbolstrip=BASS_FX_BPM_BeatSetParameters --nosymbolstrip=BASS_FX_BPM_CallbackReset --nosymbolstrip=BASS_FX_BPM_CallbackSet --nosymbolstrip=BASS_FX_BPM_DecodeGet --nosymbolstrip=BASS_FX_BPM_Free --nosymbolstrip=BASS_FX_BPM_Translate --nosymbolstrip=BASS_FX_GetVersion --nosymbolstrip=BASS_FX_ReverseCreate --nosymbolstrip=BASS_FX_ReverseGetSource --nosymbolstrip=BASS_FX_TempoCreate --nosymbolstrip=BASS_FX_TempoGetRateRatio --nosymbolstrip=BASS_FX_TempoGetSource --nosymbolstrip=BASS_Mixer_ChannelFlags --nosymbolstrip=BASS_Mixer_ChannelGetData --nosymbolstrip=BASS_Mixer_ChannelGetEnvelopePos --nosymbolstrip=BASS_Mixer_ChannelGetLevel --nosymbolstrip=BASS_Mixer_ChannelGetLevelEx --nosymbolstrip=BASS_Mixer_ChannelGetMatrix --nosymbolstrip=BASS_Mixer_ChannelGetMixer --nosymbolstrip=BASS_Mixer_ChannelGetPosition --nosymbolstrip=BASS_Mixer_ChannelGetPositionEx --nosymbolstrip=BASS_Mixer_ChannelIsActive --nosymbolstrip=BASS_Mixer_ChannelRemove --nosymbolstrip=BASS_Mixer_ChannelRemoveSync --nosymbolstrip=BASS_Mixer_ChannelSetEnvelope --nosymbolstrip=BASS_Mixer_ChannelSetEnvelopePos --nosymbolstrip=BASS_Mixer_ChannelSetMatrix --nosymbolstrip=BASS_Mixer_ChannelSetMatrixEx --nosymbolstrip=BASS_Mixer_ChannelSetPosition --nosymbolstrip=BASS_Mixer_ChannelSetSync --nosymbolstrip=BASS_Mixer_GetVersion --nosymbolstrip=BASS_Mixer_StreamAddChannel --nosymbolstrip=BASS_Mixer_StreamAddChannelEx --nosymbolstrip=BASS_Mixer_StreamCreate --nosymbolstrip=BASS_Mixer_StreamGetChannels --nosymbolstrip=BASS_Split_StreamCreate --nosymbolstrip=BASS_Split_StreamGetAvailable --nosymbolstrip=BASS_Split_StreamGetSource --nosymbolstrip=BASS_Split_StreamGetSplits --nosymbolstrip=BASS_Split_StreamReset --nosymbolstrip=BASS_Split_StreamResetEx
-
- --nolinkaway --nostrip $(GeneratedMtouchSymbolStripFlags)
-
-
- true
- full
- false
- DEBUG;ENABLE_TEST_CLOUD;
- true
- true
-
-
- pdbonly
- true
-
-
- x86_64
- None
+ true
+
+ true
+
+ $(NoWarn);MT7091
- true
- SdkOnly
- ARM64
- Entitlements.plist
+ ios-arm64
-
- true
- 25823
- false
-
-
- true
-
-
- true
- 28126
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(NoWarn);NU1605
+
+ iossimulator-x64
@@ -77,16 +24,15 @@
none
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ <_LinkerFrameworks Remove="Quartz"/>
+
+
diff --git a/osu.iOS/Application.cs b/osu.iOS/Application.cs
index c5b2d0b451..64eb5c63f5 100644
--- a/osu.iOS/Application.cs
+++ b/osu.iOS/Application.cs
@@ -3,7 +3,6 @@
#nullable disable
-using osu.Framework.iOS;
using UIKit;
namespace osu.iOS
@@ -12,7 +11,7 @@ namespace osu.iOS
{
public static void Main(string[] args)
{
- UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate));
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist
index 16cb68fa7d..c4b08ab78e 100644
--- a/osu.iOS/Info.plist
+++ b/osu.iOS/Info.plist
@@ -15,7 +15,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.4
UIDeviceFamily
1
diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs
index b3194e497b..cb3b260033 100644
--- a/osu.iOS/OsuGameIOS.cs
+++ b/osu.iOS/OsuGameIOS.cs
@@ -5,6 +5,7 @@
using System;
using Foundation;
+using Microsoft.Maui.Devices;
using osu.Framework.Graphics;
using osu.Framework.Input.Handlers;
using osu.Framework.iOS.Input;
@@ -12,7 +13,6 @@ using osu.Game;
using osu.Game.Overlays.Settings;
using osu.Game.Updater;
using osu.Game.Utils;
-using Xamarin.Essentials;
namespace osu.iOS
{
diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj
index b9da874f30..def538af1a 100644
--- a/osu.iOS/osu.iOS.csproj
+++ b/osu.iOS/osu.iOS.csproj
@@ -1,124 +1,16 @@
-
-
-
- Debug
- iPhoneSimulator
+
+
+ net6.0-ios
+ 13.4
Exe
- {3F082D0B-A964-43D7-BDF7-C256D76A50D0}
- osu.iOS
- osu.iOS
- false
+ true
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
- osu.Game
-
-
- {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}
- osu.Game.Rulesets.Catch
-
-
- {48F4582B-7687-4621-9CBE-5C24197CB536}
- osu.Game.Rulesets.Mania
-
-
- {C92A607B-1FDD-4954-9F92-03FF547D9080}
- osu.Game.Rulesets.Osu
-
-
- {F167E17A-7DE6-4AF5-B920-A5112296C695}
- osu.Game.Rulesets.Taiko
-
-
-
-
-
-
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
- false
-
-
-
-
-
-