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/diffcalc.yml b/.github/workflows/diffcalc.yml
index 4297a88e89..8461208a2e 100644
--- a/.github/workflows/diffcalc.yml
+++ b/.github/workflows/diffcalc.yml
@@ -115,7 +115,7 @@ jobs:
steps:
- name: Check permissions
run: |
- ALLOWED_USERS=(smoogipoo peppy bdach frenzibyte)
+ ALLOWED_USERS=(smoogipoo peppy bdach frenzibyte tsunyoku stanriders)
for i in "${ALLOWED_USERS[@]}"; do
if [[ "${{ github.actor }}" == "$i" ]]; then
exit 0
diff --git a/.globalconfig b/.globalconfig
deleted file mode 100644
index a4d4707f9b..0000000000
--- a/.globalconfig
+++ /dev/null
@@ -1,57 +0,0 @@
-# .NET Code Style
-# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
-
-# IDE0001: Simplify names
-dotnet_diagnostic.IDE0001.severity = warning
-
-# IDE0002: Simplify member access
-dotnet_diagnostic.IDE0002.severity = warning
-
-# IDE0003: Remove qualification
-dotnet_diagnostic.IDE0003.severity = warning
-
-# IDE0004: Remove unnecessary cast
-dotnet_diagnostic.IDE0004.severity = warning
-
-# IDE0005: Remove unnecessary imports
-dotnet_diagnostic.IDE0005.severity = warning
-
-# IDE0034: Simplify default literal
-dotnet_diagnostic.IDE0034.severity = warning
-
-# IDE0036: Sort modifiers
-dotnet_diagnostic.IDE0036.severity = warning
-
-# IDE0040: Add accessibility modifier
-dotnet_diagnostic.IDE0040.severity = warning
-
-# IDE0049: Use keyword for type name
-dotnet_diagnostic.IDE0040.severity = warning
-
-# IDE0055: Fix formatting
-dotnet_diagnostic.IDE0055.severity = warning
-
-# IDE0051: Private method is unused
-dotnet_diagnostic.IDE0051.severity = silent
-
-# IDE0052: Private member is unused
-dotnet_diagnostic.IDE0052.severity = silent
-
-# IDE0073: File header
-dotnet_diagnostic.IDE0073.severity = warning
-
-# IDE0130: Namespace mismatch with folder
-dotnet_diagnostic.IDE0130.severity = warning
-
-# IDE1006: Naming style
-dotnet_diagnostic.IDE1006.severity = warning
-
-#Disable operator overloads requiring alternate named methods
-dotnet_diagnostic.CA2225.severity = none
-
-# Banned APIs
-dotnet_diagnostic.RS0030.severity = error
-
-# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
-# See: https://github.com/ppy/osu/pull/19677
-dotnet_diagnostic.OSUF001.severity = none
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/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt
index 3c60b28765..08b79fc2c0 100644
--- a/CodeAnalysis/BannedSymbols.txt
+++ b/CodeAnalysis/BannedSymbols.txt
@@ -14,11 +14,10 @@ M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Collections.Gen
M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks.
P:System.Threading.Tasks.Task`1.Result;Don't use Task.Result. Use Task.GetResultSafely() to ensure we avoid deadlocks.
M:System.Threading.ManualResetEventSlim.Wait();Specify a timeout to avoid waiting forever.
-M:System.Char.ToLower(System.Char);char.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
-M:System.Char.ToUpper(System.Char);char.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use char.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture.
-M:System.String.ToLower();string.ToLower() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToLowerInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
-M:System.String.ToUpper();string.ToUpper() changes behaviour depending on CultureInfo.CurrentCulture. Use string.ToUpperInvariant() instead. If wanting culture-sensitive behaviour, explicitly provide CultureInfo.CurrentCulture or use LocalisableString.
M:Humanizer.InflectorExtensions.Pascalize(System.String);Humanizer's .Pascalize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToPascalCase() instead.
M:Humanizer.InflectorExtensions.Camelize(System.String);Humanizer's .Camelize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToCamelCase() instead.
M:Humanizer.InflectorExtensions.Underscore(System.String);Humanizer's .Underscore() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToSnakeCase() instead.
M:Humanizer.InflectorExtensions.Kebaberize(System.String);Humanizer's .Kebaberize() extension method changes behaviour depending on CultureInfo.CurrentCulture. Use StringDehumanizeExtensions.ToKebabCase() instead.
+M:osuTK.MathHelper.Clamp(System.Int32,System.Int32,System.Int32)~System.Int32;Use Math.Clamp() instead.
+M:osuTK.MathHelper.Clamp(System.Single,System.Single,System.Single)~System.Single;This osuTK helper has unsafe semantics when one of the bounds provided is NaN. Use Math.Clamp() instead.
+M:osuTK.MathHelper.Clamp(System.Double,System.Double,System.Double)~System.Double;This osuTK helper has unsafe semantics when one of the bounds provided is NaN. Use Math.Clamp() instead.
diff --git a/CodeAnalysis/osu.globalconfig b/CodeAnalysis/osu.globalconfig
new file mode 100644
index 0000000000..8012c31eca
--- /dev/null
+++ b/CodeAnalysis/osu.globalconfig
@@ -0,0 +1,112 @@
+# .NET Code Style
+# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
+is_global = true
+
+# IDE0001: Simplify names
+dotnet_diagnostic.IDE0001.severity = warning
+
+# IDE0002: Simplify member access
+dotnet_diagnostic.IDE0002.severity = warning
+
+# IDE0003: Remove qualification
+dotnet_diagnostic.IDE0003.severity = warning
+
+# IDE0004: Remove unnecessary cast
+dotnet_diagnostic.IDE0004.severity = warning
+
+# IDE0005: Remove unnecessary imports
+dotnet_diagnostic.IDE0005.severity = warning
+
+# IDE0034: Simplify default literal
+dotnet_diagnostic.IDE0034.severity = warning
+
+# IDE0036: Sort modifiers
+dotnet_diagnostic.IDE0036.severity = warning
+
+# IDE0040: Add accessibility modifier
+dotnet_diagnostic.IDE0040.severity = warning
+
+# IDE0049: Use keyword for type name
+dotnet_diagnostic.IDE0040.severity = warning
+
+# IDE0055: Fix formatting
+dotnet_diagnostic.IDE0055.severity = warning
+
+# IDE0051: Private method is unused
+dotnet_diagnostic.IDE0051.severity = silent
+
+# IDE0052: Private member is unused
+dotnet_diagnostic.IDE0052.severity = silent
+
+# IDE0073: File header
+dotnet_diagnostic.IDE0073.severity = warning
+
+# IDE0130: Namespace mismatch with folder
+dotnet_diagnostic.IDE0130.severity = warning
+
+# IDE1006: Naming style
+dotnet_diagnostic.IDE1006.severity = warning
+
+# CA1305: Specify IFormatProvider
+# 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
+# Flags serialization name attributes
+dotnet_diagnostic.CA1507.severity = suggestion
+
+# CA1806: Do not ignore method results
+# The usages for numeric parsing are explicitly optional
+dotnet_diagnostic.CA1806.severity = suggestion
+
+# CA1822: Mark members as static
+# Potential false positive around reflection/too much noise
+dotnet_diagnostic.CA1822.severity = none
+
+# CA1826: Do not use Enumerable method on indexable collections
+dotnet_diagnostic.CA1826.severity = suggestion
+
+# CA1859: Use concrete types when possible for improved performance
+# Involves design considerations
+dotnet_diagnostic.CA1859.severity = suggestion
+
+# CA1860: Avoid using 'Enumerable.Any()' extension method
+dotnet_diagnostic.CA1860.severity = suggestion
+
+# CA1861: Avoid constant arrays as arguments
+# Outdated with collection expressions
+dotnet_diagnostic.CA1861.severity = suggestion
+
+# CA2007: Consider calling ConfigureAwait on the awaited task
+dotnet_diagnostic.CA2007.severity = warning
+
+# CA2016: Forward the 'CancellationToken' parameter to methods
+# Some overloads are having special handling for debugger
+dotnet_diagnostic.CA2016.severity = suggestion
+
+# CA2021: Do not call Enumerable.Cast or Enumerable.OfType with incompatible types
+# Causing a lot of false positives with generics
+dotnet_diagnostic.CA2021.severity = none
+
+# CA2101: Specify marshaling for P/Invoke string arguments
+# Reports warning for all non-UTF16 usages on DllImport; consider migrating to LibraryImport
+dotnet_diagnostic.CA2101.severity = none
+
+# CA2201: Do not raise reserved exception types
+dotnet_diagnostic.CA2201.severity = warning
+
+# CA2208: Instantiate argument exceptions correctly
+dotnet_diagnostic.CA2208.severity = suggestion
+
+# CA2242: Test for NaN correctly
+dotnet_diagnostic.CA2242.severity = warning
+
+# Banned APIs
+dotnet_diagnostic.RS0030.severity = error
+
+# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
+# See: https://github.com/ppy/osu/pull/19677
+dotnet_diagnostic.OSUF001.severity = none
diff --git a/CodeAnalysis/osu.ruleset b/CodeAnalysis/osu.ruleset
deleted file mode 100644
index 6a99e230d1..0000000000
--- a/CodeAnalysis/osu.ruleset
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 5ba12b845b..3acb86ee0c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -18,9 +18,21 @@
+
+
- $(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset
+ Default
+ Default
+ Recommended
+ Recommended
+ Recommended
+ Recommended
+ Default
+ Minimum
+ Recommended
+ Default
+ Default
true
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..86f73a37d4 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..51c0233942 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..ed4e8631ea 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..51c0233942 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/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs
index 0a4fa84ce1..dd8337abee 100644
--- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs
+++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Beatmaps/PippidonBeatmapConverter.cs
@@ -1,6 +1,7 @@
// 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 System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -9,7 +10,6 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Pippidon.Objects;
using osu.Game.Rulesets.Pippidon.UI;
-using osuTK;
namespace osu.Game.Rulesets.Pippidon.Beatmaps
{
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Pippidon.Beatmaps
};
}
- private int getLane(HitObject hitObject) => (int)MathHelper.Clamp(
+ private int getLane(HitObject hitObject) => (int)Math.Clamp(
(getUsablePosition(hitObject) - minPosition) / (maxPosition - minPosition) * PippidonPlayfield.LANE_COUNT, 0, PippidonPlayfield.LANE_COUNT - 1);
private float getUsablePosition(HitObject h) => (h as IHasYPosition)?.Y ?? ((IHasXPosition)h).X;
diff --git a/osu.Android.props b/osu.Android.props
index 4699beeac0..8f219ea426 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 ccae4a15ee..8045009621 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
new file mode 100644
index 0000000000..5d309f2fc1
--- /dev/null
+++ b/osu.iOS/AppDelegate.cs
@@ -0,0 +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
+ {
+ 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 1330e29bc1..120e8caecc 100644
--- a/osu.iOS/Info.plist
+++ b/osu.iOS/Info.plist
@@ -34,9 +34,11 @@
CADisableMinimumFrameDurationOnPhone
NSCameraUsageDescription
- We don't really use the camera.
+ We don't use the camera.
NSMicrophoneUsageDescription
- We don't really use the microphone.
+ We don't use the microphone.
+ NSBluetoothAlwaysUsageDescription
+ We don't use Bluetooth.
UISupportedInterfaceOrientations
UIInterfaceOrientationLandscapeRight
@@ -68,6 +70,8 @@
sh.ppy.osu.items
+ UTTypeDescription
+ osu! replay
UTTypeIdentifier
sh.ppy.osu.osr
UTTypeTagSpecification
@@ -81,6 +85,8 @@
sh.ppy.osu.items
+ UTTypeDescription
+ osu! skin
UTTypeIdentifier
sh.ppy.osu.osk
UTTypeTagSpecification
@@ -94,6 +100,8 @@
sh.ppy.osu.items
+ UTTypeDescription
+ osu! beatmap
UTTypeIdentifier
sh.ppy.osu.osz
UTTypeTagSpecification
@@ -107,6 +115,8 @@
sh.ppy.osu.items
+ UTTypeDescription
+ osu! beatmap
UTTypeIdentifier
sh.ppy.osu.olz
UTTypeTagSpecification
@@ -143,7 +153,15 @@
Editor
+ ITSAppUsesNonExemptEncryption
+
LSApplicationCategoryType
public.app-category.music-games
+ LSSupportsOpeningDocumentsInPlace
+
+
+ GCSupportsGameMode
+
diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs
index 2a4f9b87ac..96b8fb9804 100644
--- a/osu.iOS/OsuGameIOS.cs
+++ b/osu.iOS/OsuGameIOS.cs
@@ -5,20 +5,72 @@ using System;
using Foundation;
using Microsoft.Maui.Devices;
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;
+
+ public 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();
+ protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorageIOS((IOSGameHost)host, defaultStorage);
+
protected override Edges SafeAreaOverrideEdges =>
// iOS shows a home indicator at the bottom, and adds a safe area to account for this.
// Because we have the home indicator (mostly) hidden we don't really care about drawing in this region.
diff --git a/osu.iOS/OsuStorageIOS.cs b/osu.iOS/OsuStorageIOS.cs
new file mode 100644
index 0000000000..f3a5eec737
--- /dev/null
+++ b/osu.iOS/OsuStorageIOS.cs
@@ -0,0 +1,23 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.IO;
+using osu.Framework.iOS;
+using osu.Framework.Platform;
+using osu.Game.IO;
+
+namespace osu.iOS
+{
+ public class OsuStorageIOS : OsuStorage
+ {
+ private readonly IOSGameHost host;
+
+ public OsuStorageIOS(IOSGameHost host, Storage defaultStorage)
+ : base(host, defaultStorage)
+ {
+ this.host = host;
+ }
+
+ public override Storage GetExportStorage() => new IOSStorage(Path.GetTempPath(), host);
+ }
+}
diff --git a/osu.iOS/Application.cs b/osu.iOS/Program.cs
similarity index 69%
rename from osu.iOS/Application.cs
rename to osu.iOS/Program.cs
index 74bd58acb8..fd24ecf419 100644
--- a/osu.iOS/Application.cs
+++ b/osu.iOS/Program.cs
@@ -1,15 +1,15 @@
// 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.iOS;
+using UIKit;
namespace osu.iOS
{
- public static class Application
+ public static class Program
{
public static void Main(string[] args)
{
- GameApplication.Main(new OsuGameIOS());
+ UIApplication.Main(args, null, typeof(AppDelegate));
}
}
}
diff --git a/osu.sln b/osu.sln
index 829e43fc65..63da18c23e 100644
--- a/osu.sln
+++ b/osu.sln
@@ -56,11 +56,10 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{10DF8F12-50FD-45D8-8A38-17BA764BF54D}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
- .globalconfig = .globalconfig
Directory.Build.props = Directory.Build.props
osu.Android.props = osu.Android.props
osu.iOS.props = osu.iOS.props
- CodeAnalysis\osu.ruleset = CodeAnalysis\osu.ruleset
+ global.json = global.json
osu.sln.DotSettings = osu.sln.DotSettings
osu.TestProject.props = osu.TestProject.props
EndProjectSection
@@ -95,6 +94,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Pippidon"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Pippidon.Tests", "Templates\Rulesets\ruleset-scrolling-example\osu.Game.Rulesets.Pippidon.Tests\osu.Game.Rulesets.Pippidon.Tests.csproj", "{1743BF7C-E6AE-4A06-BAD9-166D62894303}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CodeAnalysis", "CodeAnalysis", "{FB156649-D457-4D1A-969C-D3A23FD31513}"
+ ProjectSection(SolutionItems) = preProject
+ CodeAnalysis\BannedSymbols.txt = CodeAnalysis\BannedSymbols.txt
+ CodeAnalysis\osu.globalconfig = CodeAnalysis\osu.globalconfig
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
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 @@
WARNING
HINT
WARNING
- WARNING
+ HINT
WARNING
ERROR
WARNING
@@ -840,6 +840,7 @@ See the LICENCE file in the repository root for full licence text.
True
True
True
+ True
True
True
True