diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index c4ba6e5143..6ec071be2f 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -9,7 +9,7 @@
]
},
"nvika": {
- "version": "3.0.0",
+ "version": "4.0.0",
"commands": [
"nvika"
]
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d8645d728e..d75f09f184 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -114,10 +114,7 @@ jobs:
dotnet-version: "8.0.x"
- name: Install .NET workloads
- # since windows image 20241113.3.0, not specifying a version here
- # installs the .NET 7 version of android workload for very unknown reasons.
- # revisit once we upgrade to .NET 9, it's probably fixed there.
- run: dotnet workload install android --version (dotnet --version)
+ run: dotnet workload install android
- name: Compile
run: dotnet build -c Debug osu.Android.slnf
diff --git a/.idea/.idea.osu.Android/.idea/deploymentTargetSelector.xml b/.idea/.idea.osu.Android/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000000..4432459b86
--- /dev/null
+++ b/.idea/.idea.osu.Android/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CodeAnalysis/osu.globalconfig b/CodeAnalysis/osu.globalconfig
index 247a825033..8012c31eca 100644
--- a/CodeAnalysis/osu.globalconfig
+++ b/CodeAnalysis/osu.globalconfig
@@ -51,8 +51,11 @@ dotnet_diagnostic.IDE1006.severity = warning
# Too many noisy warnings for parsing/formatting numbers
dotnet_diagnostic.CA1305.severity = none
+# messagepack complains about "osu" not being title cased due to reserved words
+dotnet_diagnostic.CS8981.severity = none
+
# CA1507: Use nameof to express symbol names
-# Flaggs serialization name attributes
+# Flags serialization name attributes
dotnet_diagnostic.CA1507.severity = suggestion
# CA1806: Do not ignore method results
diff --git a/README.md b/README.md
index 6043497181..d87ca31f72 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ You can also generally download a version for your current device from the [osu!
If your platform is unsupported or not listed above, there is still a chance you can run the release or manually build it by following the instructions below.
-**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores in early 2024.
+**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores very soon so we don't have to live with this limitation.
## Developing a custom ruleset
diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
index f77cda1533..1d368e9bd1 100644
--- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
+++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj
@@ -9,9 +9,9 @@
false
-
+
-
+
diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/EmptyFreeformHitObject.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/EmptyFreeformHitObject.cs
index 9cd18d2d9f..0699f5d039 100644
--- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/EmptyFreeformHitObject.cs
+++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform/Objects/EmptyFreeformHitObject.cs
@@ -14,7 +14,16 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects
public Vector2 Position { get; set; }
- public float X => Position.X;
- public float Y => Position.Y;
+ public float X
+ {
+ get => Position.X;
+ set => Position = new Vector2(value, Y);
+ }
+
+ public float Y
+ {
+ get => Position.Y;
+ set => Position = new Vector2(X, value);
+ }
}
}
diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 47cabaddb1..d69bc78b8f 100644
--- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -9,9 +9,9 @@
false
-
+
-
+
diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/PippidonHitObject.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/PippidonHitObject.cs
index 0c22554e82..f938d26b26 100644
--- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/PippidonHitObject.cs
+++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/PippidonHitObject.cs
@@ -14,7 +14,16 @@ namespace osu.Game.Rulesets.Pippidon.Objects
public Vector2 Position { get; set; }
- public float X => Position.X;
- public float Y => Position.Y;
+ public float X
+ {
+ get => Position.X;
+ set => Position = new Vector2(value, Y);
+ }
+
+ public float Y
+ {
+ get => Position.Y;
+ set => Position = new Vector2(X, value);
+ }
}
}
diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
index a7d62291d0..7ac269f65f 100644
--- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj
@@ -9,9 +9,9 @@
false
-
+
-
+
diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
index 47cabaddb1..d69bc78b8f 100644
--- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
+++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj
@@ -9,9 +9,9 @@
false
-
+
-
+
diff --git a/osu.Android.props b/osu.Android.props
index f13760bd21..6bbd432ee7 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -10,7 +10,7 @@
true
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 3e618a3a74..ca2604858c 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -17,6 +17,6 @@
-all
-
+
diff --git a/osu.iOS/AppDelegate.cs b/osu.iOS/AppDelegate.cs
index e88b39f710..5d309f2fc1 100644
--- a/osu.iOS/AppDelegate.cs
+++ b/osu.iOS/AppDelegate.cs
@@ -1,14 +1,61 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using Foundation;
using osu.Framework.iOS;
+using UIKit;
namespace osu.iOS
{
[Register("AppDelegate")]
public class AppDelegate : GameApplicationDelegate
{
- protected override Framework.Game CreateGame() => new OsuGameIOS();
+ private UIInterfaceOrientationMask? defaultOrientationsMask;
+ private UIInterfaceOrientationMask? orientations;
+
+ ///
+ /// The current orientation the game is displayed in.
+ ///
+ public UIInterfaceOrientation CurrentOrientation => Host.Window.UIWindow.WindowScene!.InterfaceOrientation;
+
+ ///
+ /// Controls the orientations allowed for the device to rotate to, overriding the default allowed orientations.
+ ///
+ public UIInterfaceOrientationMask? Orientations
+ {
+ get => orientations;
+ set
+ {
+ if (orientations == value)
+ return;
+
+ orientations = value;
+
+ if (OperatingSystem.IsIOSVersionAtLeast(16))
+ Host.Window.ViewController.SetNeedsUpdateOfSupportedInterfaceOrientations();
+ else
+ UIViewController.AttemptRotationToDeviceOrientation();
+ }
+ }
+
+ protected override Framework.Game CreateGame() => new OsuGameIOS(this);
+
+ public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations(UIApplication application, UIWindow forWindow)
+ {
+ if (orientations != null)
+ return orientations.Value;
+
+ if (defaultOrientationsMask == null)
+ {
+ defaultOrientationsMask = 0;
+ var defaultOrientations = (NSArray)NSBundle.MainBundle.ObjectForInfoDictionary("UISupportedInterfaceOrientations");
+
+ foreach (var value in defaultOrientations.ToArray())
+ defaultOrientationsMask |= Enum.Parse(value.ToString().Replace("UIInterfaceOrientation", string.Empty));
+ }
+
+ return defaultOrientationsMask.Value;
+ }
}
}
diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist
index 29410938a3..120e8caecc 100644
--- a/osu.iOS/Info.plist
+++ b/osu.iOS/Info.plist
@@ -153,9 +153,15 @@
Editor
+ ITSAppUsesNonExemptEncryption
+ LSApplicationCategoryTypepublic.app-category.music-gamesLSSupportsOpeningDocumentsInPlace
+
+ GCSupportsGameMode
+
diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs
index c0bd77366e..883e89e38a 100644
--- a/osu.iOS/OsuGameIOS.cs
+++ b/osu.iOS/OsuGameIOS.cs
@@ -8,15 +8,63 @@ using osu.Framework.Graphics;
using osu.Framework.iOS;
using osu.Framework.Platform;
using osu.Game;
+using osu.Game.Screens;
using osu.Game.Updater;
using osu.Game.Utils;
+using osuTK;
+using UIKit;
namespace osu.iOS
{
public partial class OsuGameIOS : OsuGame
{
+ private readonly AppDelegate appDelegate;
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
+ public override bool HideUnlicensedContent => true;
+
+ protected override Vector2 ScalingContainerTargetDrawSize => new Vector2(1024, 1024 * DrawHeight / DrawWidth);
+
+ public OsuGameIOS(AppDelegate appDelegate)
+ {
+ this.appDelegate = appDelegate;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ UserPlayingState.BindValueChanged(_ => updateOrientation());
+ }
+
+ protected override void ScreenChanged(IOsuScreen? current, IOsuScreen? newScreen)
+ {
+ base.ScreenChanged(current, newScreen);
+
+ if (newScreen != null)
+ updateOrientation();
+ }
+
+ private void updateOrientation() => UIApplication.SharedApplication.InvokeOnMainThread(() =>
+ {
+ bool iPad = UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad;
+ var orientation = MobileUtils.GetOrientation(this, (IOsuScreen)ScreenStack.CurrentScreen, iPad);
+
+ switch (orientation)
+ {
+ case MobileUtils.Orientation.Locked:
+ appDelegate.Orientations = (UIInterfaceOrientationMask)(1 << (int)appDelegate.CurrentOrientation);
+ break;
+
+ case MobileUtils.Orientation.Portrait:
+ appDelegate.Orientations = UIInterfaceOrientationMask.Portrait;
+ break;
+
+ case MobileUtils.Orientation.Default:
+ appDelegate.Orientations = null;
+ break;
+ }
+ });
+
protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier();
protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index ccd6db354b..5cac0024b7 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -170,7 +170,7 @@
WARNINGHINTWARNING
- WARNING
+ HINTWARNINGERRORWARNING
@@ -840,6 +840,7 @@ See the LICENCE file in the repository root for full licence text.
TrueTrueTrue
+ TrueTrueTrueTrue