Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,6 @@ jobs:
-p:BuildIpa=false
-p:CodesignKey=""
-p:CodesignProvision=""
-p:ServerGarbageCollection=true
-p:ConcurrentGarbageCollection=true

- name: Package iOS build
run: |
Expand Down
2 changes: 2 additions & 0 deletions osu.Desktop/DiscordRichPresence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using DiscordRPC;
using DiscordRPC.Message;
Expand All @@ -27,6 +28,7 @@

namespace osu.Desktop
{
[RequiresUnreferencedCode("Newtonsoft.Json relies on reflection over types that may be removed when trimming.")]
internal partial class DiscordRichPresence : Component
{
private const string client_id = "1216669957799018608";
Expand Down
2 changes: 2 additions & 0 deletions osu.Desktop/LegacyIpc/LegacyTcpIpcProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json.Linq;
using osu.Framework.Logging;
Expand All @@ -20,6 +21,7 @@ namespace osu.Desktop.LegacyIpc
/// <summary>
/// Provides IPC to legacy osu! clients.
/// </summary>
[RequiresUnreferencedCode("Newtonsoft.Json relies on reflection over types that may be removed when trimming.")]
public class LegacyTcpIpcProvider : TcpIpcProvider
{
private static readonly Logger logger = Logger.GetLogger("legacy-ipc");
Expand Down
3 changes: 1 addition & 2 deletions osu.Desktop/MacOS/MacOSAppLocationChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.IO;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
Expand All @@ -26,7 +25,7 @@ protected override void LoadComplete()
{
base.LoadComplete();

string assemblyPath = RuntimeInfo.EntryAssembly.Location;
string assemblyPath = AppContext.BaseDirectory;

bool inRootApp = assemblyPath.StartsWith("/Applications/", StringComparison.Ordinal);
bool inUserApp = assemblyPath.StartsWith(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Applications/"), StringComparison.Ordinal);
Expand Down
3 changes: 2 additions & 1 deletion osu.Desktop/Windows/Icons.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.IO;

namespace osu.Desktop.Windows
Expand All @@ -10,7 +11,7 @@ public static class Icons
/// <summary>
/// Fully qualified path to the directory that contains icons (in the installation folder).
/// </summary>
private static readonly string icon_directory = Path.GetDirectoryName(typeof(Icons).Assembly.Location)!;
private static readonly string icon_directory = AppContext.BaseDirectory;

public static string Lazer => Path.Join(icon_directory, "lazer.ico");

Expand Down
2 changes: 1 addition & 1 deletion osu.Desktop/Windows/WindowsAssociationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static class WindowsAssociationManager
/// </summary>
internal const string SHELL_OPEN_COMMAND = @"Shell\Open\Command";

private static readonly string exe_path = Path.ChangeExtension(typeof(WindowsAssociationManager).Assembly.Location, ".exe").Replace('/', '\\');
private static readonly string exe_path = Path.Combine(AppContext.BaseDirectory, Path.ChangeExtension(AppDomain.CurrentDomain.FriendlyName, ".exe")).Replace('/', '\\');

/// <summary>
/// Program ID prefix used for file associations. Should be relatively short since the full program ID has a 39 character limit,
Expand Down
2 changes: 2 additions & 0 deletions osu.Desktop/Windows/WindowsKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

// ReSharper disable IdentifierTypo
Expand Down Expand Up @@ -47,6 +48,7 @@ private static int lowLevelKeyboardProc(int nCode, int wParam, ref KdDllHookStru
return callNextHookEx(0, nCode, wParam, ref lParam);
}

[RequiresAssemblyFiles("Uses Marshal.GetHINSTANCE which requires the assembly to be loaded from disk.")]
internal static void Disable()
{
if (keyHook != IntPtr.Zero || isBlocked)
Expand Down
3 changes: 2 additions & 1 deletion osu.Game.Benchmarks/BenchmarkScoreMultiplierCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
Expand Down Expand Up @@ -42,7 +43,7 @@ public record ModTestCase(string Description, IEnumerable<Mod> Mods)
public override void SetUp()
{
base.SetUp();
calculator = new OsuRuleset().CreateScoreMultiplierCalculator(new ScoreMultiplierContext());
calculator = new OsuRuleset().CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));
}

[Benchmark]
Expand Down
3 changes: 2 additions & 1 deletion osu.Game.Rulesets.Mania.Tests/ManiaScoreMultiplierTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
Expand Down Expand Up @@ -212,7 +213,7 @@ [new DateTimeOffset(2026, 7, 18, 0, 20, 15, 0, TimeSpan.Zero), "2026.522.1-tachy
[TestCaseSource(nameof(key_mod_multiplier_test_cases))]
public void TestKeyModMultiplierCompatibility(DateTimeOffset endDate, string clientVersion, double expectedMultiplier)
{
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new ScoreInfo
var calculator = Ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty(), new ScoreInfo
{
Date = endDate,
ClientVersion = clientVersion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void TestHitWindowWithDoubleTime()
Mod = doubleTime,
PassCondition = () => Player.ScoreProcessor.JudgedHits > 0
&& Player.ScoreProcessor.Accuracy.Value == 1
&& Player.ScoreProcessor.TotalScore.Value == (long)(1_000_000 * new ManiaScoreMultiplierCalculator(new ScoreMultiplierContext()).CalculateFor([doubleTime])),
&& Player.ScoreProcessor.TotalScore.Value == (long)(1_000_000 * new ManiaScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor([doubleTime])),
Autoplay = false,
CreateBeatmap = () => new Beatmap
{
Expand Down
36 changes: 28 additions & 8 deletions osu.Game.Tests/Rulesets/Scoring/ScoreMultiplierCalculatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.

using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
Expand All @@ -13,7 +14,7 @@ public class ScoreMultiplierCalculatorTest
[Test]
public void TestFlatMultiplier()
{
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext());
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));

double multiplier = calculator.CalculateFor([new OsuModEasy()]);

Expand All @@ -23,36 +24,55 @@ public void TestFlatMultiplier()
[Test]
public void TestSettingDependentMultiplier()
{
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext());
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));

double multiplier = calculator.CalculateFor([new OsuModDaycore { SpeedChange = { Value = 0.6 } }]);

Assert.That(multiplier, Is.EqualTo(0.4));
}

[Test]
public void TestContextDependentMultiplier()
public void TestScoreDependentMultiplier()
{
TestScoreMultiplierCalculator calculator;

double multiplier;

Assert.Multiple(() =>
{
calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext());
calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));
multiplier = calculator.CalculateFor([new OsuModHardRock()]);
Assert.That(multiplier, Is.EqualTo(1.4));

calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new ScoreInfo { ClientVersion = "2024.123.0" }));
calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty(), new ScoreInfo { ClientVersion = "2024.123.0" }));
multiplier = calculator.CalculateFor([new OsuModHardRock()]);
Assert.That(multiplier, Is.EqualTo(1.2));
});
}

[Test]
public void TestDifficultyDependentMultiplier()
{
TestScoreMultiplierCalculator calculator;

double multiplier;

Assert.Multiple(() =>
{
calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));
multiplier = calculator.CalculateFor([new OsuModEasy()]);
Assert.That(multiplier, Is.EqualTo(0.15));

calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty { ApproachRate = 0 }));
multiplier = calculator.CalculateFor([new OsuModEasy()]);
Assert.That(multiplier, Is.EqualTo(0.1));
});
}

[Test]
public void TestCombinationMultiplier()
{
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext());
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));

double multiplier = calculator.CalculateFor([new OsuModEasy(), new OsuModDaycore()]);

Expand All @@ -62,7 +82,7 @@ public void TestCombinationMultiplier()
[Test]
public void TestCombinationAndFlatMultipliers()
{
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext());
var calculator = new TestScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));

double multiplier = calculator.CalculateFor([new OsuModDaycore(), new OsuModHardRock(), new OsuModEasy()]);

Expand All @@ -74,7 +94,7 @@ private class TestScoreMultiplierCalculator : ScoreMultiplierCalculator
public TestScoreMultiplierCalculator(ScoreMultiplierContext context)
: base(context)
{
Single<OsuModEasy>(hasMultiplier: 0.15);
Single<OsuModEasy>(hasMultiplier: context.BeatmapDifficultyWithoutMods.ApproachRate == 0 ? 0.1 : 0.15);
Single<OsuModDaycore>(hasMultiplier: daycore => (1 + daycore.SpeedChange.Value) / 4);
Single<OsuModHardRock>(hasMultiplier: _ => context.Score?.ClientVersion == "2024.123.0" ? 1.2 : 1.4);
Combination<OsuModEasy, OsuModDaycore>(hasMultiplier: (_, _) => 0.003);
Expand Down
34 changes: 34 additions & 0 deletions osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,29 @@ public void TestComboAccounting([Values] bool shuffleResults)
Assert.That(scoreProcessor.HighestCombo.Value, Is.Zero);
}

[Test]
public void TestScoreMultiplier()
{
Mod[] mods = new Mod[] { new OsuModHardRock() };

scoreProcessor = new TestScoreProcessor();
scoreProcessor.Mods.Value = mods;

var workingBeatmap = new TestWorkingBeatmap(beatmap);
var playableBeatmap = workingBeatmap.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, mods);

scoreProcessor.ApplyBeatmap(playableBeatmap);

var judgementResult = new JudgementResult(beatmap.HitObjects.Single(), new OsuJudgement())
{
Type = HitResult.Great,
};
scoreProcessor.ApplyResult(judgementResult);

Assert.That(scoreProcessor.MaximumTotalScore, Is.EqualTo(1_000_000 * 1.1).Within(0.5d));
Assert.That(scoreProcessor.GetDisplayScore(ScoringMode.Standardised), Is.EqualTo(1_000_000 * 1.1).Within(0.5d));
}

private class TestJudgement : Judgement
{
public override HitResult MaxResult { get; }
Expand Down Expand Up @@ -537,9 +560,20 @@ private class TestRuleset : Ruleset

public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();

public override ScoreMultiplierCalculator CreateScoreMultiplierCalculator(ScoreMultiplierContext context) => new TestScoreMultiplierCalculator(context);

public override string Description => string.Empty;
public override string ShortName => string.Empty;
}

private class TestScoreMultiplierCalculator : ScoreMultiplierCalculator
{
public TestScoreMultiplierCalculator(ScoreMultiplierContext context)
: base(context)
{
Single<OsuModHardRock>(hasMultiplier: context.BeatmapDifficultyWithoutMods.CircleSize == 4 ? 1.1 : 1.0);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
Expand Down Expand Up @@ -119,7 +120,7 @@ public void TestUnrankedBadge()

private void assertModsMultiplier(Ruleset ruleset, IEnumerable<Mod> mods)
{
var scoreMultiplierCalculator = ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext());
var scoreMultiplierCalculator = ruleset.CreateScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty()));
double multiplier = scoreMultiplierCalculator.CalculateFor(mods);
string expectedValue = ModUtils.FormatScoreMultiplier(multiplier).ToString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
Expand Down Expand Up @@ -123,7 +124,7 @@ public void TestPreexistingSelection()
AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
AddAssert("mod multiplier correct", () =>
{
double multiplier = new OsuScoreMultiplierCalculator(new ScoreMultiplierContext()).CalculateFor(SelectedMods.Value);
double multiplier = new OsuScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor(SelectedMods.Value);
return Precision.AlmostEquals(multiplier, this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
});
assertCustomisationToggleState(disabled: false, active: false);
Expand All @@ -138,7 +139,7 @@ public void TestExternalSelection()
AddUntilStep("two panels active", () => modSelectOverlay.ChildrenOfType<ModPanel>().Count(panel => panel.Active.Value) == 2);
AddAssert("mod multiplier correct", () =>
{
double multiplier = new OsuScoreMultiplierCalculator(new ScoreMultiplierContext()).CalculateFor(SelectedMods.Value);
double multiplier = new OsuScoreMultiplierCalculator(new ScoreMultiplierContext(new BeatmapDifficulty())).CalculateFor(SelectedMods.Value);
return Precision.AlmostEquals(multiplier, this.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value);
});
assertCustomisationToggleState(disabled: false, active: false);
Expand Down
10 changes: 8 additions & 2 deletions osu.Game/Configuration/SettingSourceAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
Expand Down Expand Up @@ -42,9 +43,10 @@ public class SettingSourceAttribute : Attribute, IComparable<SettingSourceAttrib
/// <remarks>
/// Must be a type deriving <see cref="SettingsItem{T}"/> with a public parameterless constructor.
/// </remarks>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
public Type? SettingControlType { get; set; }

public SettingSourceAttribute(Type declaringType, string label, string? description = null)
public SettingSourceAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type declaringType, string label, string? description = null)
{
Label = getLocalisableStringFromMember(label) ?? string.Empty;
Description = getLocalisableStringFromMember(description) ?? string.Empty;
Expand Down Expand Up @@ -109,6 +111,7 @@ public int CompareTo(SettingSourceAttribute? other)

public static partial class SettingSourceExtensions
{
[RequiresUnreferencedCode("SettingSourceAttribute uses reflection to instantiate settings controls and may not be compatible with trimming.")]
public static IEnumerable<Drawable> CreateSettingsControls(this object obj)
{
foreach (var (attr, property) in obj.GetOrderedSettingsSourceProperties())
Expand Down Expand Up @@ -222,6 +225,7 @@ public static IEnumerable<Drawable> CreateSettingsControls(this object obj)
/// Can be used for serialization and equality comparison purposes.
/// </summary>
/// <param name="setting">A <see cref="SettingSourceAttribute"/> bindable.</param>
[RequiresUnreferencedCode("Uses BindableValueAccessor which relies on reflection and may not be compatible with trimming.")]
public static object GetUnderlyingSettingValue(this object setting)
{
switch (setting)
Expand Down Expand Up @@ -250,6 +254,7 @@ public static object GetUnderlyingSettingValue(this object setting)
}
}

[RequiresUnreferencedCode("SettingSourceAttribute uses reflection to retrieve properties and may not be compatible with trimming.")]
public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj)
{
var type = obj.GetType();
Expand All @@ -260,7 +265,7 @@ public static object GetUnderlyingSettingValue(this object setting)
return properties;
}

private static IEnumerable<(SettingSourceAttribute, PropertyInfo)> getSettingsSourceProperties(Type type)
private static IEnumerable<(SettingSourceAttribute, PropertyInfo)> getSettingsSourceProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type)
{
foreach (var property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
{
Expand All @@ -273,6 +278,7 @@ public static object GetUnderlyingSettingValue(this object setting)
}
}

[RequiresUnreferencedCode("SettingSourceAttribute uses reflection to retrieve properties and may not be compatible with trimming.")]
public static ICollection<(SettingSourceAttribute, PropertyInfo)> GetOrderedSettingsSourceProperties(this object obj)
=> obj.GetSettingsSourceProperties()
.OrderBy(attr => attr.Item1)
Expand Down
Loading
Loading