1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-22 13:10:12 +08:00

Compare commits

...

311 Commits

248 changed files with 3670 additions and 2307 deletions
+2 -2
View File
@@ -15,7 +15,7 @@
]
},
"smoogipoo.nvika": {
"version": "1.0.1",
"version": "1.0.3",
"commands": [
"nvika"
]
@@ -33,4 +33,4 @@
]
}
}
}
}
+1 -1
View File
@@ -16,7 +16,7 @@
<EmbeddedResource Include="Resources\**\*.*" />
</ItemGroup>
<ItemGroup Label="Code Analysis">
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.2" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3" PrivateAssets="All" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="5.0.3" PrivateAssets="All" />
</ItemGroup>
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
+1 -1
View File
@@ -52,7 +52,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1026.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1026.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1103.0" />
</ItemGroup>
<ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
+1
View File
@@ -20,6 +20,7 @@ namespace osu.Android
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
@@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
}
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad" } },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
}
@@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public double Distance => Path.Distance;
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
public IList<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
public double? LegacyLastTickOffset { get; set; }
}
@@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
return null;
case CatchSkinComponents.Catcher:
decimal version = GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1;
decimal version = GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value ?? 1;
if (version < 2.3m)
{
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private IList<string> getSampleNames(IList<HitSampleInfo> hitSampleInfo)
=> hitSampleInfo.Select(sample => sample.LookupNames.First()).ToList();
private IList<IList<string>> getNodeSampleNames(List<IList<HitSampleInfo>> hitSampleInfo)
private IList<IList<string>> getNodeSampleNames(IList<IList<HitSampleInfo>> hitSampleInfo)
=> hitSampleInfo?.Select(getSampleNames)
.ToList();
@@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
@@ -488,7 +488,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to retrieve node samples at.</param>
private List<IList<HitSampleInfo>> nodeSamplesAt(int time)
private IList<IList<HitSampleInfo>> nodeSamplesAt(int time)
{
if (!(HitObject is IHasPathWithRepeats curveData))
return null;
@@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
};
}
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
{
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus" } },
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
};
}
+1 -1
View File
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.Objects
}
}
public List<IList<HitSampleInfo>> NodeSamples { get; set; }
public IList<IList<HitSampleInfo>> NodeSamples { get; set; }
/// <summary>
/// The head note of the hold.
@@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
float rightLineWidth = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1;
bool hasLeftLine = leftLineWidth > 0;
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|| isLastColumn;
Color4 lineColour = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White;
@@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{
this.beatmap = (ManiaBeatmap)beatmap;
isLegacySkin = new Lazy<bool>(() => GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
isLegacySkin = new Lazy<bool>(() => GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version) != null);
hasKeyTexture = new Lazy<bool>(() =>
{
string keyImage = this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1";
@@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
[TestCase(6.5867229481955389d, "diffcalc-test")]
[TestCase(1.0416315570967911d, "zero-length-sliders")]
[TestCase(6.5295339534769958d, "diffcalc-test")]
[TestCase(1.1514260533755143d, "zero-length-sliders")]
public void Test(double expected, string name)
=> base.Test(expected, name);
[TestCase(8.2730989071947896d, "diffcalc-test")]
[TestCase(1.2726413186221039d, "zero-length-sliders")]
[TestCase(9.047752485219954d, "diffcalc-test")]
[TestCase(1.3985711787077566d, "zero-length-sliders")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new OsuModDoubleTime());
@@ -0,0 +1,34 @@
// 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestSceneNoSpinnerStacking : TestSceneOsuPlayer
{
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 10 },
Ruleset = ruleset
}
};
for (int i = 0; i < 512; i++)
{
if (i % 32 < 20)
beatmap.HitObjects.Add(new Spinner { Position = new Vector2(256, 192), StartTime = i * 200, EndTime = (i * 200) + 100 });
}
return beatmap;
}
}
}
@@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
@@ -12,20 +12,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
{
public class OsuDifficultyHitObject : DifficultyHitObject
{
private const int normalized_radius = 52;
private const int normalized_radius = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths.
private const int min_delta_time = 25;
protected new OsuHitObject BaseObject => (OsuHitObject)base.BaseObject;
/// <summary>
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms to account for simultaneous <see cref="OsuDifficultyHitObject"/>s.
/// </summary>
public double StrainTime { get; private set; }
/// <summary>
/// Normalized distance from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
/// </summary>
public double JumpDistance { get; private set; }
/// <summary>
/// Minimum distance from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
/// </summary>
public double MovementDistance { get; private set; }
/// <summary>
/// Normalized distance between the start and end position of the previous <see cref="OsuDifficultyHitObject"/>.
/// </summary>
@@ -37,6 +38,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
/// </summary>
public double? Angle { get; private set; }
/// <summary>
/// Milliseconds elapsed since the end time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
/// </summary>
public double MovementTime { get; private set; }
/// <summary>
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/> to the end time of the same previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
/// </summary>
public double TravelTime { get; private set; }
/// <summary>
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 25ms.
/// </summary>
public readonly double StrainTime;
private readonly OsuHitObject lastLastObject;
private readonly OsuHitObject lastObject;
@@ -46,13 +62,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
this.lastLastObject = (OsuHitObject)lastLastObject;
this.lastObject = (OsuHitObject)lastObject;
setDistances();
// Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects.
StrainTime = Math.Max(DeltaTime, min_delta_time);
// Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects.
StrainTime = Math.Max(DeltaTime, 25);
setDistances(clockRate);
}
private void setDistances()
private void setDistances(double clockRate)
{
// We don't need to calculate either angle or distance when one of the last->curr objects is a spinner
if (BaseObject is Spinner || lastObject is Spinner)
@@ -67,15 +83,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
scalingFactor *= 1 + smallCircleBonus;
}
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
if (lastObject is Slider lastSlider)
{
computeSliderCursorPosition(lastSlider);
TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
TravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, min_delta_time);
MovementTime = Math.Max(StrainTime - TravelTime, min_delta_time);
// Jump distance from the slider tail to the next object, as opposed to the lazy position of JumpDistance.
float tailJumpDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor;
// For hitobjects which continue in the direction of the slider, the player will normally follow through the slider,
// such that they're not jumping from the lazy position but rather from very close to (or the end of) the slider.
// In such cases, a leniency is applied by also considering the jump distance from the tail of the slider, and taking the minimum jump distance.
MovementDistance = Math.Min(JumpDistance, tailJumpDistance);
}
else
{
MovementTime = StrainTime;
MovementDistance = JumpDistance;
}
Vector2 lastCursorPosition = getEndCursorPosition(lastObject);
JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
if (lastLastObject != null && !(lastLastObject is Spinner))
{
@@ -98,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
slider.LazyEndPosition = slider.StackedPosition;
float approxFollowCircleRadius = (float)(slider.Radius * 3);
float followCircleRadius = (float)(slider.Radius * 2.4);
var computeVertex = new Action<double>(t =>
{
double progress = (t - slider.StartTime) / slider.SpanDuration;
@@ -111,11 +141,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
float dist = diff.Length;
if (dist > approxFollowCircleRadius)
slider.LazyTravelTime = t - slider.StartTime;
if (dist > followCircleRadius)
{
// The cursor would be outside the follow circle, we need to move it
diff.Normalize(); // Obtain direction of diff
dist -= approxFollowCircleRadius;
dist -= followCircleRadius;
slider.LazyEndPosition += diff * dist;
slider.LazyTravelDistance += dist;
}
+65 -22
View File
@@ -14,53 +14,96 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
/// </summary>
public class Aim : OsuStrainSkill
{
private const double angle_bonus_begin = Math.PI / 3;
private const double timing_threshold = 107;
public Aim(Mod[] mods)
: base(mods)
{
}
protected override int HistoryLength => 2;
private const double wide_angle_multiplier = 1.5;
private const double acute_angle_multiplier = 2.0;
private double currentStrain = 1;
private double skillMultiplier => 26.25;
private double skillMultiplier => 23.25;
private double strainDecayBase => 0.15;
private double strainValueOf(DifficultyHitObject current)
{
if (current.BaseObject is Spinner)
if (current.BaseObject is Spinner || Previous.Count <= 1 || Previous[0].BaseObject is Spinner)
return 0;
var osuCurrent = (OsuDifficultyHitObject)current;
var osuCurrObj = (OsuDifficultyHitObject)current;
var osuLastObj = (OsuDifficultyHitObject)Previous[0];
var osuLastLastObj = (OsuDifficultyHitObject)Previous[1];
double aimStrain = 0;
// Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle.
double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime;
if (Previous.Count > 0)
// But if the last object is a slider, then we extend the travel velocity through the slider into the current object.
if (osuLastObj.BaseObject is Slider)
{
var osuPrevious = (OsuDifficultyHitObject)Previous[0];
double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to current object
double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to slider end.
if (osuCurrent.Angle != null && osuCurrent.Angle.Value > angle_bonus_begin)
currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity.
}
// As above, do the same for the previous hitobject.
double prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime;
if (osuLastLastObj.BaseObject is Slider)
{
double movementVelocity = osuLastObj.MovementDistance / osuLastObj.MovementTime;
double travelVelocity = osuLastObj.TravelDistance / osuLastObj.TravelTime;
prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity);
}
double angleBonus = 0;
double aimStrain = currVelocity; // Start strain with regular velocity.
if (Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime)) // If rhythms are the same.
{
if (osuCurrObj.Angle != null && osuLastObj.Angle != null && osuLastLastObj.Angle != null)
{
const double scale = 90;
double currAngle = osuCurrObj.Angle.Value;
double lastAngle = osuLastObj.Angle.Value;
double lastLastAngle = osuLastLastObj.Angle.Value;
double angleBonus = Math.Sqrt(
Math.Max(osuPrevious.JumpDistance - scale, 0)
* Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2)
* Math.Max(osuCurrent.JumpDistance - scale, 0));
aimStrain = 1.4 * applyDiminishingExp(Math.Max(0, angleBonus)) / Math.Max(timing_threshold, osuPrevious.StrainTime);
// Rewarding angles, take the smaller velocity as base.
angleBonus = Math.Min(currVelocity, prevVelocity);
double wideAngleBonus = calcWideAngleBonus(currAngle);
double acuteAngleBonus = calcAcuteAngleBonus(currAngle);
if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
acuteAngleBonus = 0;
else
{
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
}
wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastLastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastLastAngle gets more obtuse.
angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the angle buffs together.
}
}
double jumpDistanceExp = applyDiminishingExp(osuCurrent.JumpDistance);
double travelDistanceExp = applyDiminishingExp(osuCurrent.TravelDistance);
aimStrain += angleBonus; // Add in angle bonus.
return Math.Max(
aimStrain + (jumpDistanceExp + travelDistanceExp + Math.Sqrt(travelDistanceExp * jumpDistanceExp)) / Math.Max(osuCurrent.StrainTime, timing_threshold),
(Math.Sqrt(travelDistanceExp * jumpDistanceExp) + jumpDistanceExp + travelDistanceExp) / osuCurrent.StrainTime
);
return aimStrain;
}
private double calcWideAngleBonus(double angle) => Math.Pow(Math.Sin(3.0 / 4 * (Math.Min(5.0 / 6 * Math.PI, Math.Max(Math.PI / 6, angle)) - Math.PI / 6)), 2);
private double calcAcuteAngleBonus(double angle) => 1 - calcWideAngleBonus(angle);
private double applyDiminishingExp(double val) => Math.Pow(val, 0.99);
private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
@@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects
set => StackHeightBindable.Value = value;
}
public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
public double Radius => OBJECT_RADIUS * Scale;
+7 -1
View File
@@ -79,7 +79,13 @@ namespace osu.Game.Rulesets.Osu.Objects
/// </summary>
internal float LazyTravelDistance;
public List<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
/// <summary>
/// The time taken by the cursor upon completion of this <see cref="Slider"/> if it was hit
/// with as few movements as possible. This is set and used by difficulty calculation.
/// </summary>
internal double LazyTravelTime;
public IList<IList<HitSampleInfo>> NodeSamples { get; set; } = new List<IList<HitSampleInfo>>();
[JsonIgnore]
public IList<HitSampleInfo> TailSamples { get; private set; }
+3
View File
@@ -8,6 +8,7 @@ using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.Objects
/// </summary>
public int MaximumBonusSpins { get; protected set; } = 1;
public override Vector2 StackOffset => Vector2.Zero;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
@@ -14,7 +14,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
using static osu.Game.Skinning.LegacySkinConfiguration;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
@@ -158,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
if (hasNumber)
{
decimal? legacyVersion = skin.GetConfig<LegacySetting, decimal>(LegacySetting.Version)?.Value;
decimal? legacyVersion = skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value;
if (legacyVersion >= 2.0m)
// legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
@@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
if (shouldConvertSliderToHits(obj, beatmap, distanceData, out int taikoDuration, out double tickSpacing))
{
List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
IList<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
int i = 0;
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
// because the right half is flipped, we need to position using width - position to get the true "topleft" origin position
float negativeScaleAdjust = content.Width / ratio;
if (skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
if (skin.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
{
left.Centre.Position = new Vector2(0, taiko_bar_y) * ratio;
right.Centre.Position = new Vector2(negativeScaleAdjust - 56, taiko_bar_y) * ratio;
@@ -7,7 +7,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Resources;
using static osu.Game.Skinning.LegacySkinConfiguration;
using static osu.Game.Skinning.SkinConfiguration;
namespace osu.Game.Tests.Gameplay
{
@@ -8,6 +8,7 @@ using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Online.API;
using osu.Game.Online.Solo;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
@@ -88,33 +89,27 @@ namespace osu.Game.Tests.Online
}
[Test]
public void TestDeserialiseScoreInfoWithEmptyMods()
public void TestDeserialiseSubmittableScoreWithEmptyMods()
{
var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo };
var score = new SubmittableScore(new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo });
var deserialised = JsonConvert.DeserializeObject<ScoreInfo>(JsonConvert.SerializeObject(score));
if (deserialised != null)
deserialised.Ruleset = new OsuRuleset().RulesetInfo;
var deserialised = JsonConvert.DeserializeObject<SubmittableScore>(JsonConvert.SerializeObject(score));
Assert.That(deserialised?.Mods.Length, Is.Zero);
}
[Test]
public void TestDeserialiseScoreInfoWithCustomModSetting()
public void TestDeserialiseSubmittableScoreWithCustomModSetting()
{
var score = new ScoreInfo
var score = new SubmittableScore(new ScoreInfo
{
Ruleset = new OsuRuleset().RulesetInfo,
Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } }
};
});
var deserialised = JsonConvert.DeserializeObject<ScoreInfo>(JsonConvert.SerializeObject(score));
var deserialised = JsonConvert.DeserializeObject<SubmittableScore>(JsonConvert.SerializeObject(score));
if (deserialised != null)
deserialised.Ruleset = new OsuRuleset().RulesetInfo;
Assert.That(((OsuModDoubleTime)deserialised?.Mods[0])?.SpeedChange.Value, Is.EqualTo(2));
Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2));
}
private class TestRuleset : Ruleset
@@ -128,7 +128,7 @@ namespace osu.Game.Tests.Online
private void addAvailabilityCheckStep(string description, Func<BeatmapAvailability> expected)
{
AddAssert(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke()));
AddUntilStep(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke()));
}
private static BeatmapInfo getTestBeatmapInfo(string archiveFile)
+190 -128
View File
@@ -4,10 +4,12 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Platform;
using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Skinning;
using SharpCompress.Archives.Zip;
@@ -16,169 +18,213 @@ namespace osu.Game.Tests.Skins.IO
{
public class ImportSkinTest : ImportTest
{
[Test]
public async Task TestBasicImport()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk"));
Assert.That(imported.Name, Is.EqualTo("test skin"));
Assert.That(imported.Creator, Is.EqualTo("skinner"));
}
finally
{
host.Exit();
}
}
}
#region Testing filename metadata inclusion
[Test]
public async Task TestImportTwiceWithSameMetadata()
public Task TestSingleImportDifferentFilename() => runSkinTest(async osu =>
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk"));
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(1));
// the first should be overwritten by the second import.
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
host.Exit();
}
}
}
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu);
});
[Test]
public async Task TestImportTwiceWithNoMetadata()
public Task TestSingleImportWeirdIniFileCase() => runSkinTest(async osu =>
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", iniFilename: "Skin.InI"), "skin.osk"));
// if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety.
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk"));
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(2));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
host.Exit();
}
}
}
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu);
});
[Test]
public async Task TestImportTwiceWithDifferentMetadata()
public Task TestSingleImportMissingSectionHeader() => runSkinTest(async osu =>
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", includeSectionHeader: false), "skin.osk"));
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2", "skinner"), "skin.osk"));
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk"));
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(2));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
}
finally
{
host.Exit();
}
}
}
// When the import filename doesn't match, it should be appended (and update the skin.ini).
assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu);
});
[Test]
public async Task TestImportUpperCasedOskArchive()
public Task TestSingleImportMatchingFilename() => runSkinTest(async osu =>
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "test skin.osk"));
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.OsK"));
Assert.That(imported.Name, Is.EqualTo("name 1"));
Assert.That(imported.Creator, Is.EqualTo("author 1"));
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.oSK"));
Assert.That(imported2.Hash, Is.EqualTo(imported.Hash));
}
finally
{
host.Exit();
}
}
}
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata(import1, "test skin", "skinner", osu);
});
[Test]
public async Task TestSameMetadataNameDifferentFolderName()
public Task TestSingleImportNoIniFile() => runSkinTest(async osu =>
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
{
try
{
var osu = LoadOsuIntoHost(host);
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithNonIniFile(), "test skin.osk"));
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 1"));
Assert.That(imported.Name, Is.EqualTo("name 1 [my custom skin 1]"));
Assert.That(imported.Creator, Is.EqualTo("author 1"));
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata(import1, "test skin", "Unknown", osu);
});
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 2"));
Assert.That(imported2.Name, Is.EqualTo("name 1 [my custom skin 2]"));
Assert.That(imported2.Creator, Is.EqualTo("author 1"));
[Test]
public Task TestEmptyImportImportsWithFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createEmptyOsk(), "test skin.osk"));
Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash));
}
finally
{
host.Exit();
}
}
// When the import filename matches it shouldn't be appended.
assertCorrectMetadata(import1, "test skin", "Unknown", osu);
});
#endregion
#region Cases where imports should match existing
[Test]
public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
assertImportedOnce(import1, import2);
});
[Test]
public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => runSkinTest(async osu =>
{
// if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety.
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk"));
assertImportedOnce(import1, import2);
});
[Test]
public Task TestImportUpperCasedOskArchive() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.OsK"));
assertCorrectMetadata(import1, "name 1", "author 1", osu);
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.oSK"));
assertImportedOnce(import1, import2);
});
[Test]
public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
assertImportedOnce(import1, import2);
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
});
#endregion
#region Cases where imports should be uniquely imported
[Test]
public Task TestImportTwiceWithSameMetadataButDifferentFilename() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin2.osk"));
assertImportedBoth(import1, import2);
});
[Test]
public Task TestImportTwiceWithNoMetadataDifferentDownloadFilename() => runSkinTest(async osu =>
{
// if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety.
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download2.osk"));
assertImportedBoth(import1, import2);
});
[Test]
public Task TestImportTwiceWithSameFilenameDifferentMetadata() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2", "skinner"), "skin.osk"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2.1", "skinner"), "skin.osk"));
assertImportedBoth(import1, import2);
assertCorrectMetadata(import1, "test skin v2 [skin]", "skinner", osu);
assertCorrectMetadata(import2, "test skin v2.1 [skin]", "skinner", osu);
});
[Test]
public Task TestSameMetadataNameDifferentFolderName() => runSkinTest(async osu =>
{
var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1"));
var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 2"));
assertImportedBoth(import1, import2);
assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu);
assertCorrectMetadata(import2, "name 1 [my custom skin 2]", "author 1", osu);
});
#endregion
private void assertCorrectMetadata(SkinInfo import1, string name, string creator, OsuGameBase osu)
{
Assert.That(import1.Name, Is.EqualTo(name));
Assert.That(import1.Creator, Is.EqualTo(creator));
// for extra safety let's reconstruct the skin, reading from the skin.ini.
var instance = import1.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager)));
Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name));
Assert.That(instance.Configuration.SkinInfo.Creator, Is.EqualTo(creator));
}
private MemoryStream createOsk(string name, string author, bool makeUnique = true)
private void assertImportedBoth(SkinInfo import1, SkinInfo import2)
{
Assert.That(import2.ID, Is.Not.EqualTo(import1.ID));
Assert.That(import2.Hash, Is.Not.EqualTo(import1.Hash));
Assert.That(import2.Files.Select(f => f.FileInfoID), Is.Not.EquivalentTo(import1.Files.Select(f => f.FileInfoID)));
}
private void assertImportedOnce(SkinInfo import1, SkinInfo import2)
{
Assert.That(import2.ID, Is.EqualTo(import1.ID));
Assert.That(import2.Hash, Is.EqualTo(import1.Hash));
Assert.That(import2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(import1.Files.Select(f => f.FileInfoID)));
}
private MemoryStream createEmptyOsk()
{
var zipStream = new MemoryStream();
using var zip = ZipArchive.Create();
zip.AddEntry("skin.ini", generateSkinIni(name, author, makeUnique));
zip.SaveTo(zipStream);
return zipStream;
}
private MemoryStream generateSkinIni(string name, string author, bool makeUnique = true)
private MemoryStream createOskWithNonIniFile()
{
var zipStream = new MemoryStream();
using var zip = ZipArchive.Create();
zip.AddEntry("hitcircle.png", new MemoryStream(new byte[] { 0, 1, 2, 3 }));
zip.SaveTo(zipStream);
return zipStream;
}
private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = @"skin.ini", bool includeSectionHeader = true)
{
var zipStream = new MemoryStream();
using var zip = ZipArchive.Create();
zip.AddEntry(iniFilename, generateSkinIni(name, author, makeUnique, includeSectionHeader));
zip.SaveTo(zipStream);
return zipStream;
}
private MemoryStream generateSkinIni(string name, string author, bool makeUnique = true, bool includeSectionHeader = true)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.WriteLine("[General]");
if (includeSectionHeader)
writer.WriteLine("[General]");
writer.WriteLine($"Name: {name}");
writer.WriteLine($"Author: {author}");
@@ -193,6 +239,22 @@ namespace osu.Game.Tests.Skins.IO
return stream;
}
private async Task runSkinTest(Func<OsuGameBase, Task> action, [CallerMemberName] string callingMethodName = @"")
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(callingMethodName))
{
try
{
var osu = LoadOsuIntoHost(host);
await action(osu);
}
finally
{
host.Exit();
}
}
}
private async Task<SkinInfo> loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null)
{
var skinManager = osu.Dependencies.Get<SkinManager>();
@@ -106,7 +106,7 @@ namespace osu.Game.Tests.Skins
var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource("skin-latest.ini"))
using (var stream = new LineBufferedReader(resStream))
Assert.AreEqual(LegacySkinConfiguration.LATEST_VERSION, decoder.Decode(stream).LegacyVersion);
Assert.AreEqual(SkinConfiguration.LATEST_VERSION, decoder.Decode(stream).LegacyVersion);
}
[Test]
@@ -151,7 +151,7 @@ namespace osu.Game.Tests.Skins
{
AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m);
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
AddAssert("Check legacy version lookup", () => requester.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
}
[Test]
@@ -160,7 +160,7 @@ namespace osu.Game.Tests.Skins
// completely ignoring beatmap versions for simplicity.
AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m);
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = 1.7m);
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
AddAssert("Check legacy version lookup", () => requester.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value == 2.3m);
}
[Test]
@@ -169,14 +169,14 @@ namespace osu.Game.Tests.Skins
AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = null);
AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null);
AddAssert("Check legacy version lookup",
() => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == LegacySkinConfiguration.LATEST_VERSION);
() => requester.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value == SkinConfiguration.LATEST_VERSION);
}
[Test]
public void TestIniWithNoVersionFallsBackTo1()
{
AddStep("Parse skin with no version", () => userSource.Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(new MemoryStream())));
AddAssert("Check legacy version lookup", () => requester.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.0m);
AddAssert("Check legacy version lookup", () => requester.GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value == 1.0m);
}
public enum LookupType
@@ -0,0 +1,181 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Beatmaps
{
public class TestSceneBeatmapCard : OsuTestScene
{
private APIBeatmapSet[] testCases;
#region Test case generation
[BackgroundDependencyLoader]
private void load()
{
var normal = CreateAPIBeatmapSet(Ruleset.Value);
normal.HasVideo = true;
normal.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet();
var someDifficulties = getManyDifficultiesBeatmapSet(11);
someDifficulties.Title = someDifficulties.TitleUnicode = "some difficulties";
someDifficulties.Status = BeatmapSetOnlineStatus.Qualified;
var manyDifficulties = getManyDifficultiesBeatmapSet(100);
manyDifficulties.Status = BeatmapSetOnlineStatus.Pending;
var explicitMap = CreateAPIBeatmapSet(Ruleset.Value);
explicitMap.HasExplicitContent = true;
var featuredMap = CreateAPIBeatmapSet(Ruleset.Value);
featuredMap.TrackId = 1;
var explicitFeaturedMap = CreateAPIBeatmapSet(Ruleset.Value);
explicitFeaturedMap.HasExplicitContent = true;
explicitFeaturedMap.TrackId = 2;
var longName = CreateAPIBeatmapSet(Ruleset.Value);
longName.Title = longName.TitleUnicode = "this track has an incredibly and implausibly long title";
longName.Artist = longName.ArtistUnicode = "and this artist! who would have thunk it. it's really such a long name.";
longName.HasExplicitContent = true;
longName.TrackId = 444;
testCases = new[]
{
normal,
undownloadable,
someDifficulties,
manyDifficulties,
explicitMap,
featuredMap,
explicitFeaturedMap,
longName
};
}
private APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet
{
OnlineID = 123,
Title = "undownloadable beatmap",
Artist = "test",
Source = "more tests",
Author = new User
{
Username = "BanchoBot",
Id = 3,
},
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
},
Preview = @"https://b.ppy.sh/preview/12345.mp3",
PlayCount = 123,
FavouriteCount = 456,
BPM = 111,
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = new List<APIBeatmap>
{
new APIBeatmap
{
RulesetID = Ruleset.Value.OnlineID,
DifficultyName = "Test",
StarRating = 6.42,
}
}
};
private static APIBeatmapSet getManyDifficultiesBeatmapSet(int count)
{
var beatmaps = new List<APIBeatmap>();
for (int i = 0; i < count; i++)
{
beatmaps.Add(new APIBeatmap
{
RulesetID = i % 4,
StarRating = 2 + i % 4 * 2,
});
}
return new APIBeatmapSet
{
OnlineID = 1,
Title = "many difficulties beatmap",
Artist = "test",
Author = new User
{
Username = "BanchoBot",
Id = 3,
},
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = beatmaps,
};
}
#endregion
private Drawable createContent(OverlayColourScheme colourScheme, Func<APIBeatmapSet, Drawable> creationFunc)
{
var colourProvider = new OverlayColourProvider(colourScheme);
return new DependencyProvidingContainer
{
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[]
{
(typeof(OverlayColourProvider), colourProvider)
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5
},
new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Padding = new MarginPadding(10),
Spacing = new Vector2(10),
ChildrenEnumerable = testCases.Select(creationFunc)
}
}
}
};
}
private void createTestCase(Func<APIBeatmapSet, Drawable> creationFunc)
{
foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast<OverlayColourScheme>())
AddStep($"set {scheme} scheme", () => Child = createContent(scheme, creationFunc));
}
[Test]
public void TestNormal() => createTestCase(beatmapSetInfo => new BeatmapCard(beatmapSetInfo));
}
}
@@ -13,6 +13,7 @@ using osu.Framework.Bindables;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Ranking;
using osuTK.Input;
@@ -132,11 +133,12 @@ namespace osu.Game.Tests.Visual.Gameplay
private ScoreInfo getScoreInfo(bool replayAvailable)
{
return new APILegacyScoreInfo
return new APIScoreInfo
{
OnlineScoreID = 2553163309,
OnlineRulesetID = 0,
Replay = replayAvailable,
OnlineID = 2553163309,
RulesetID = 0,
Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(),
HasReplay = replayAvailable,
User = new User
{
Id = 39828,
@@ -38,8 +38,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait();
}
[Test]
@@ -204,7 +202,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestDownloadButtonHiddenWhenBeatmapExists()
{
createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo);
var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo;
AddStep("import beatmap", () => manager.Import(beatmap.BeatmapSet).Wait());
createPlaylist(beatmap);
assertDownloadButtonVisible(false);
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
{
protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private RoomsContainer container;
@@ -23,8 +23,6 @@ using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Lounge.Components;
using osu.Game.Screens.OnlinePlay.Match;
@@ -32,6 +30,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Spectate;
using osu.Game.Tests.Resources;
using osu.Game.Users;
using osuTK.Input;
@@ -44,11 +43,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
private RulesetStore rulesets;
private BeatmapSetInfo importedSet;
private DependenciesScreen dependenciesScreen;
private TestMultiplayer multiplayerScreen;
private TestMultiplayerClient client;
private TestMultiplayerScreenStack multiplayerScreenStack;
private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager;
private TestMultiplayerClient client => multiplayerScreenStack.Client;
private TestMultiplayerRoomManager roomManager => multiplayerScreenStack.RoomManager;
[Cached(typeof(UserLookupCache))]
private UserLookupCache lookupCache = new TestUserLookupCache();
@@ -70,22 +68,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
});
AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer());
AddStep("load dependencies", () =>
{
client = new TestMultiplayerClient(roomManager);
// The screen gets suspended so it stops receiving updates.
Child = client;
LoadScreen(dependenciesScreen = new DependenciesScreen(client));
});
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded);
AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack()));
AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded);
AddUntilStep("wait for lounge to load", () => this.ChildrenOfType<MultiplayerLoungeSubScreen>().FirstOrDefault()?.IsLoaded == true);
}
@@ -247,6 +231,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
}
});
AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1);
AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
}
[Test]
@@ -306,6 +293,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddUntilStep("wait for join", () => client.Room != null);
AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1);
AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1);
}
[Test]
@@ -441,7 +431,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("start match externally", () => client.StartMatch());
AddAssert("play not started", () => multiplayerScreen.IsCurrentScreen());
AddAssert("play not started", () => multiplayerScreenStack.IsCurrentScreen());
}
[Test]
@@ -485,7 +475,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
});
AddUntilStep("play started", () => !multiplayerScreen.IsCurrentScreen());
AddUntilStep("play started", () => multiplayerScreenStack.CurrentScreen is SpectatorScreen);
}
[Test]
@@ -527,16 +517,16 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("open mod overlay", () => this.ChildrenOfType<RoomSubScreen.UserModSelectButton>().Single().TriggerClick());
AddStep("invoke on back button", () => multiplayerScreen.OnBackButton());
AddStep("invoke on back button", () => multiplayerScreenStack.OnBackButton());
AddAssert("mod overlay is hidden", () => this.ChildrenOfType<UserModSelectOverlay>().Single().State.Value == Visibility.Hidden);
AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);
testLeave("back button", () => multiplayerScreen.OnBackButton());
testLeave("back button", () => multiplayerScreenStack.OnBackButton());
// mimics home button and OS window close
testLeave("forced exit", () => multiplayerScreen.Exit());
testLeave("forced exit", () => multiplayerScreenStack.Exit());
void testLeave(string actionName, Action action)
{
@@ -577,7 +567,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("click start button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("wait for player", () => Stack.CurrentScreen is Player);
AddUntilStep("wait for player", () => multiplayerScreenStack.CurrentScreen is Player);
// Gameplay runs in real-time, so we need to incrementally check if gameplay has finished in order to not time out.
for (double i = 1000; i < TestResources.QUICK_BEATMAP_LENGTH; i += 1000)
@@ -586,15 +576,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep($"wait for time > {i}", () => this.ChildrenOfType<GameplayClockContainer>().SingleOrDefault()?.GameplayClock.CurrentTime > time);
}
AddUntilStep("wait for results", () => Stack.CurrentScreen is ResultsScreen);
AddUntilStep("wait for results", () => multiplayerScreenStack.CurrentScreen is ResultsScreen);
}
private MultiplayerReadyButton readyButton => this.ChildrenOfType<MultiplayerReadyButton>().Single();
private void createRoom(Func<Room> room)
{
AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
AddStep("open room", () => multiplayerScreen.ChildrenOfType<LoungeSubScreen>().Single().Open(room()));
AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
AddStep("open room", () => multiplayerScreenStack.ChildrenOfType<LoungeSubScreen>().Single().Open(room()));
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddWaitStep("wait for transition", 2);
@@ -607,26 +597,5 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => client.Room != null);
}
/// <summary>
/// Used for the sole purpose of adding <see cref="TestMultiplayerClient"/> as a resolvable dependency.
/// </summary>
private class DependenciesScreen : OsuScreen
{
[Cached(typeof(MultiplayerClient))]
public readonly TestMultiplayerClient Client;
public DependenciesScreen(TestMultiplayerClient client)
{
Client = client;
}
}
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
{
public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; }
protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager();
}
}
}
@@ -8,7 +8,6 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Configuration;
using osu.Game.Online.API;
@@ -40,9 +39,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
}
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result);
AddStep("create leaderboard", () =>
@@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
@@ -44,9 +43,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
return room;
}
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result);
AddStep("create leaderboard", () =>
@@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
{
protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private LoungeSubScreen loungeScreen;
@@ -4,7 +4,6 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
namespace osu.Game.Tests.Visual.Multiplayer
@@ -14,8 +13,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
[SetUp]
public new void Setup() => Schedule(() =>
{
SelectedRoom.Value = new Room();
Child = new Container
{
Anchor = Anchor.Centre,
@@ -45,11 +45,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
}
[Test]
public void TestAddNullUser()
public void TestAddUnresolvedUser()
{
AddAssert("one unique panel", () => this.ChildrenOfType<ParticipantPanel>().Select(p => p.User).Distinct().Count() == 1);
AddStep("add non-resolvable user", () => Client.AddNullUser());
AddStep("add non-resolvable user", () => Client.TestAddUnresolvedUser());
AddAssert("null user added", () => Client.Room.AsNonNull().Users.Count(u => u.User == null) == 1);
AddUntilStep("two unique panels", () => this.ChildrenOfType<ParticipantPanel>().Select(p => p.User).Distinct().Count() == 2);
@@ -6,18 +6,14 @@ using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.OnlinePlay.Multiplayer;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
@@ -33,9 +29,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
private RulesetStore rulesets;
private BeatmapSetInfo importedSet;
private DependenciesScreen dependenciesScreen;
private TestMultiplayer multiplayerScreen;
private TestMultiplayerClient client;
private TestMultiplayerScreenStack multiplayerScreenStack;
private TestMultiplayerClient client => multiplayerScreenStack.Client;
[Cached(typeof(UserLookupCache))]
private UserLookupCache lookupCache = new TestUserLookupCache();
@@ -57,24 +53,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First();
});
AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer());
AddStep("load dependencies", () =>
{
client = new TestMultiplayerClient(multiplayerScreen.RoomManager);
// The screen gets suspended so it stops receiving updates.
Child = client;
LoadScreen(dependenciesScreen = new DependenciesScreen(client));
});
AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen);
AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.NotLoaded);
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded);
AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack()));
AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded);
AddUntilStep("wait for lounge to load", () => this.ChildrenOfType<MultiplayerLoungeSubScreen>().FirstOrDefault()?.IsLoaded == true);
}
@@ -120,7 +100,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddStep("press button", () =>
{
InputManager.MoveMouseTo(multiplayerScreen.ChildrenOfType<TeamDisplay>().First());
InputManager.MoveMouseTo(multiplayerScreenStack.ChildrenOfType<TeamDisplay>().First());
InputManager.Click(MouseButton.Left);
});
AddAssert("user on team 1", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1);
@@ -154,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
private void createRoom(Func<Room> room)
{
AddStep("open room", () => multiplayerScreen.ChildrenOfType<LoungeSubScreen>().Single().Open(room()));
AddStep("open room", () => multiplayerScreenStack.ChildrenOfType<LoungeSubScreen>().Single().Open(room()));
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
AddWaitStep("wait for transition", 2);
@@ -167,26 +147,5 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for join", () => client.Room != null);
}
/// <summary>
/// Used for the sole purpose of adding <see cref="TestMultiplayerClient"/> as a resolvable dependency.
/// </summary>
private class DependenciesScreen : OsuScreen
{
[Cached(typeof(MultiplayerClient))]
public readonly TestMultiplayerClient Client;
public DependenciesScreen(TestMultiplayerClient client)
{
Client = client;
}
}
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
{
public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; }
protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager();
}
}
}
@@ -9,21 +9,18 @@ using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Toolbar;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Menu;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Lounge;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Options;
using osu.Game.Tests.Beatmaps.IO;
using osu.Game.Tests.Visual.Multiplayer;
using osuTK;
using osuTK.Input;
@@ -333,12 +330,12 @@ namespace osu.Game.Tests.Visual.Navigation
[Test]
public void TestPushMatchSubScreenAndPressBackButtonImmediately()
{
TestMultiplayer multiplayer = null;
TestMultiplayerScreenStack multiplayerScreenStack = null;
PushAndConfirm(() => multiplayer = new TestMultiplayer());
PushAndConfirm(() => multiplayerScreenStack = new TestMultiplayerScreenStack());
AddUntilStep("wait for lounge", () => multiplayer.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
AddStep("open room", () => multiplayer.ChildrenOfType<LoungeSubScreen>().Single().Open());
AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
AddStep("open room", () => multiplayerScreenStack.ChildrenOfType<LoungeSubScreen>().Single().Open());
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
AddWaitStep("wait two frames", 2);
}
@@ -453,18 +450,5 @@ namespace osu.Game.Tests.Visual.Navigation
protected override bool DisplayStableImportPrompt => false;
}
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
{
[Cached(typeof(MultiplayerClient))]
public readonly TestMultiplayerClient Client;
public TestMultiplayer()
{
Client = new TestMultiplayerClient((TestRequestHandlingMultiplayerRoomManager)RoomManager);
}
protected override RoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager();
}
}
}
@@ -21,15 +21,12 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestUndownloadableWithLink()
{
AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo
AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new APIBeatmapSet
{
OnlineInfo = new APIBeatmapSet
Availability = new BeatmapSetOnlineAvailability
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
ExternalLink = @"https://osu.ppy.sh",
},
DownloadDisabled = true,
ExternalLink = @"https://osu.ppy.sh",
},
});
@@ -39,14 +36,11 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestUndownloadableNoLink()
{
AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new BeatmapSetInfo
AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new APIBeatmapSet
{
OnlineInfo = new APIBeatmapSet
Availability = new BeatmapSetOnlineAvailability
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
},
DownloadDisabled = true,
},
});
@@ -56,15 +50,12 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestPartsRemovedWithLink()
{
AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo
AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new APIBeatmapSet
{
OnlineInfo = new APIBeatmapSet
Availability = new BeatmapSetOnlineAvailability
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = false,
ExternalLink = @"https://osu.ppy.sh",
},
DownloadDisabled = false,
ExternalLink = @"https://osu.ppy.sh",
},
});
@@ -74,14 +65,11 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestNormal()
{
AddStep("set normal beatmapset", () => container.BeatmapSet = new BeatmapSetInfo
AddStep("set normal beatmapset", () => container.BeatmapSet = new APIBeatmapSet
{
OnlineInfo = new APIBeatmapSet
Availability = new BeatmapSetOnlineAvailability
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = false,
},
DownloadDisabled = false,
},
});
@@ -1,15 +1,15 @@
// 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.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Tests.Visual.Online
{
@@ -35,9 +35,9 @@ namespace osu.Game.Tests.Visual.Online
AddStep("load multiple rulesets beatmapset", () =>
{
selector.BeatmapSet = new BeatmapSetInfo
selector.BeatmapSet = new APIBeatmapSet
{
Beatmaps = enabledRulesets.Select(r => new BeatmapInfo { Ruleset = r }).ToList()
Beatmaps = enabledRulesets.Select(r => new APIBeatmap { RulesetID = r.OnlineID }).ToList()
};
});
@@ -53,13 +53,13 @@ namespace osu.Game.Tests.Visual.Online
AddStep("load single ruleset beatmapset", () =>
{
selector.BeatmapSet = new BeatmapSetInfo
selector.BeatmapSet = new APIBeatmapSet
{
Beatmaps = new List<BeatmapInfo>
Beatmaps = new List<APIBeatmap>
{
new BeatmapInfo
new APIBeatmap
{
Ruleset = enabledRuleset
RulesetID = enabledRuleset.OnlineID
}
}
};
@@ -71,9 +71,9 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestEmptyBeatmapSet()
{
AddStep("load empty beatmapset", () => selector.BeatmapSet = new BeatmapSetInfo
AddStep("load empty beatmapset", () => selector.BeatmapSet = new APIBeatmapSet
{
Beatmaps = new List<BeatmapInfo>()
Beatmaps = new List<APIBeatmap>()
});
AddAssert("no ruleset selected", () => selector.SelectedTab == null);
@@ -49,60 +49,48 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep(@"show first", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
overlay.ShowBeatmapSet(new APIBeatmapSet
{
OnlineBeatmapSetID = 1235,
Metadata = new BeatmapMetadata
OnlineID = 1235,
Title = @"an awesome beatmap",
Artist = @"naru narusegawa",
Source = @"hinata sou",
Tags = @"test tag tag more tag",
Author = new User
{
Title = @"an awesome beatmap",
Artist = @"naru narusegawa",
Source = @"hinata sou",
Tags = @"test tag tag more tag",
Author = new User
{
Username = @"BanchoBot",
Id = 3,
},
Username = @"BanchoBot",
Id = 3,
},
OnlineInfo = new APIBeatmapSet
Preview = @"https://b.ppy.sh/preview/12345.mp3",
PlayCount = 123,
FavouriteCount = 456,
Submitted = DateTime.Now,
Ranked = DateTime.Now,
BPM = 111,
HasVideo = true,
Ratings = Enumerable.Range(0, 11).ToArray(),
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = new List<APIBeatmap>
{
Preview = @"https://b.ppy.sh/preview/12345.mp3",
PlayCount = 123,
FavouriteCount = 456,
Submitted = DateTime.Now,
Ranked = DateTime.Now,
BPM = 111,
HasVideo = true,
Ratings = Enumerable.Range(0, 11).ToArray(),
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
new APIBeatmap
{
StarDifficulty = 9.99,
Version = @"TEST",
StarRating = 9.99,
DifficultyName = @"TEST",
Length = 456000,
Ruleset = rulesets.GetRuleset(3),
BaseDifficulty = new BeatmapDifficulty
RulesetID = 3,
CircleSize = 1,
DrainRate = 2.3f,
OverallDifficulty = 4.5f,
ApproachRate = 6,
CircleCount = 111,
SliderCount = 12,
PlayCount = 222,
PassCount = 21,
FailTimes = new APIFailTimes
{
CircleSize = 1,
DrainRate = 2.3f,
OverallDifficulty = 4.5f,
ApproachRate = 6,
},
OnlineInfo = new APIBeatmap
{
CircleCount = 111,
SliderCount = 12,
PlayCount = 222,
PassCount = 21,
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
},
},
@@ -120,71 +108,15 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep(@"show undownloadable", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
var set = getBeatmapSet();
set.Availability = new BeatmapSetOnlineAvailability
{
OnlineBeatmapSetID = 1234,
Metadata = new BeatmapMetadata
{
Title = @"undownloadable beatmap",
Artist = @"no one",
Source = @"some source",
Tags = @"another test tag tag more test tags",
Author = new User
{
Username = @"BanchoBot",
Id = 3,
},
},
OnlineInfo = new APIBeatmapSet
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
ExternalLink = "https://osu.ppy.sh",
},
Preview = @"https://b.ppy.sh/preview/1234.mp3",
PlayCount = 123,
FavouriteCount = 456,
Submitted = DateTime.Now,
Ranked = DateTime.Now,
BPM = 111,
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Language = new BeatmapSetOnlineLanguage { Id = 3, Name = "English" },
Genre = new BeatmapSetOnlineGenre { Id = 4, Name = "Rock" },
Ratings = Enumerable.Range(0, 11).ToArray(),
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
StarDifficulty = 5.67,
Version = @"ANOTHER TEST",
Length = 123000,
Ruleset = rulesets.GetRuleset(1),
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 9,
DrainRate = 8,
OverallDifficulty = 7,
ApproachRate = 6,
},
OnlineInfo = new APIBeatmap
{
CircleCount = 123,
SliderCount = 45,
PlayCount = 567,
PassCount = 89,
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
},
},
},
});
DownloadDisabled = true,
ExternalLink = "https://osu.ppy.sh",
};
overlay.ShowBeatmapSet(set);
});
downloadAssert(false);
@@ -195,48 +127,30 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("show multiple rulesets beatmap", () =>
{
var beatmaps = new List<BeatmapInfo>();
var beatmaps = new List<APIBeatmap>();
foreach (var ruleset in rulesets.AvailableRulesets.Skip(1))
{
beatmaps.Add(new BeatmapInfo
beatmaps.Add(new APIBeatmap
{
Version = ruleset.Name,
Ruleset = ruleset,
BaseDifficulty = new BeatmapDifficulty(),
OnlineInfo = new APIBeatmap
DifficultyName = ruleset.Name,
RulesetID = ruleset.OnlineID,
FailTimes = new APIFailTimes
{
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
});
}
overlay.ShowBeatmapSet(new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"multiple rulesets beatmap",
Artist = @"none",
Author = new User
{
Username = "BanchoBot",
Id = 3,
}
},
OnlineInfo = new APIBeatmapSet
{
Covers = new BeatmapSetOnlineCovers(),
Ratings = Enumerable.Range(0, 11).ToArray(),
},
Beatmaps = beatmaps
});
var set = getBeatmapSet();
set.Beatmaps = beatmaps;
overlay.ShowBeatmapSet(set);
});
AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.BeatmapInfo.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value)));
AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.OnlineID == overlay.Header.RulesetSelector.Current.Value.OnlineID));
AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected);
}
@@ -246,7 +160,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("show explicit map", () =>
{
var beatmapSet = getBeatmapSet();
beatmapSet.OnlineInfo.HasExplicitContent = true;
beatmapSet.HasExplicitContent = true;
overlay.ShowBeatmapSet(beatmapSet);
});
}
@@ -257,7 +171,7 @@ namespace osu.Game.Tests.Visual.Online
AddStep("show featured map", () =>
{
var beatmapSet = getBeatmapSet();
beatmapSet.OnlineInfo.TrackId = 1;
beatmapSet.TrackId = 1;
overlay.ShowBeatmapSet(beatmapSet);
});
}
@@ -274,63 +188,41 @@ namespace osu.Game.Tests.Visual.Online
AddStep(@"show without reload", overlay.Show);
}
private BeatmapSetInfo createManyDifficultiesBeatmapSet()
private APIBeatmapSet createManyDifficultiesBeatmapSet()
{
var beatmaps = new List<BeatmapInfo>();
var set = getBeatmapSet();
var beatmaps = new List<APIBeatmap>();
for (int i = 1; i < 41; i++)
{
beatmaps.Add(new BeatmapInfo
beatmaps.Add(new APIBeatmap
{
OnlineBeatmapID = i * 10,
Version = $"Test #{i}",
Ruleset = Ruleset.Value,
StarDifficulty = 2 + i * 0.1,
BaseDifficulty = new BeatmapDifficulty
OnlineID = i * 10,
DifficultyName = $"Test #{i}",
RulesetID = Ruleset.Value.ID ?? -1,
StarRating = 2 + i * 0.1,
OverallDifficulty = 3.5f,
FailTimes = new APIFailTimes
{
OverallDifficulty = 3.5f,
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
},
OnlineInfo = new APIBeatmap
{
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(),
},
}
});
}
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 123,
Metadata = new BeatmapMetadata
{
Title = @"many difficulties beatmap",
Artist = @"none",
Author = new User
{
Username = @"BanchoBot",
Id = 3,
},
},
OnlineInfo = new APIBeatmapSet
{
Preview = @"https://b.ppy.sh/preview/123.mp3",
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Ratings = Enumerable.Range(0, 11).ToArray(),
},
Beatmaps = beatmaps,
};
set.Beatmaps = beatmaps;
return set;
}
private BeatmapSetInfo getBeatmapSet()
private APIBeatmapSet getBeatmapSet()
{
var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value);
// Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`).
beatmapSet.OnlineBeatmapSetID = nextBeatmapSetId++;
beatmapSet.OnlineID = nextBeatmapSetId++;
return beatmapSet;
}
@@ -44,27 +44,21 @@ namespace osu.Game.Tests.Visual.Online
AddStep("set second set", () => details.BeatmapSet = secondSet);
AddAssert("ratings set", () => details.Ratings.Ratings == secondSet.Ratings);
static BeatmapSetInfo createSet() => new BeatmapSetInfo
static APIBeatmapSet createSet() => new APIBeatmapSet
{
Beatmaps = new List<BeatmapInfo>
Beatmaps = new List<APIBeatmap>
{
new BeatmapInfo
new APIBeatmap
{
OnlineInfo = new APIBeatmap
FailTimes = new APIFailTimes
{
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
},
}
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
},
}
},
OnlineInfo = new APIBeatmapSet
{
Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(),
Status = BeatmapSetOnlineStatus.Ranked
}
Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(),
Status = BeatmapSetOnlineStatus.Ranked
};
}
@@ -59,21 +59,18 @@ namespace osu.Game.Tests.Visual.Online
var firstBeatmap = createBeatmap();
var secondBeatmap = createBeatmap();
AddStep("set first set", () => successRate.BeatmapInfo = firstBeatmap);
AddStep("set first set", () => successRate.Beatmap = firstBeatmap);
AddAssert("ratings set", () => successRate.Graph.FailTimes == firstBeatmap.FailTimes);
AddStep("set second set", () => successRate.BeatmapInfo = secondBeatmap);
AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
AddAssert("ratings set", () => successRate.Graph.FailTimes == secondBeatmap.FailTimes);
static BeatmapInfo createBeatmap() => new BeatmapInfo
static APIBeatmap createBeatmap() => new APIBeatmap
{
OnlineInfo = new APIBeatmap
FailTimes = new APIFailTimes
{
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
}
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
}
};
}
@@ -81,14 +78,11 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestOnlyFailMetrics()
{
AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo
AddStep("set beatmap", () => successRate.Beatmap = new APIBeatmap
{
OnlineInfo = new APIBeatmap
FailTimes = new APIFailTimes
{
FailTimes = new APIFailTimes
{
Fails = Enumerable.Range(1, 100).ToArray(),
}
Fails = Enumerable.Range(1, 100).ToArray(),
}
});
@@ -98,12 +92,9 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestEmptyMetrics()
{
AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo
AddStep("set beatmap", () => successRate.Beatmap = new APIBeatmap
{
OnlineInfo = new APIBeatmap
{
FailTimes = new APIFailTimes(),
}
FailTimes = new APIFailTimes()
});
AddAssert("graph max values correct", () => successRate.ChildrenOfType<BarGraph>().All(graph => graph.MaxValue == 0));
@@ -69,24 +69,7 @@ namespace osu.Game.Tests.Visual.Online
AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => downloadButton.DownloadEnabled == enabled);
}
private BeatmapSetInfo createSoleily()
{
return new BeatmapSetInfo
{
ID = 1,
OnlineBeatmapSetID = 241526,
OnlineInfo = new APIBeatmapSet
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = false,
ExternalLink = string.Empty,
},
},
};
}
private void createButtonWithBeatmap(BeatmapSetInfo beatmap)
private void createButtonWithBeatmap(IBeatmapSetInfo beatmap)
{
AddStep("create button", () =>
{
@@ -112,32 +95,47 @@ namespace osu.Game.Tests.Visual.Online
});
}
private BeatmapSetInfo getDownloadableBeatmapSet()
private IBeatmapSetInfo createSoleily()
{
var normal = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo;
normal.OnlineInfo.HasVideo = true;
normal.OnlineInfo.HasStoryboard = true;
return normal;
return new APIBeatmapSet
{
OnlineID = 241526,
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = false,
ExternalLink = string.Empty,
},
};
}
private BeatmapSetInfo getUndownloadableBeatmapSet()
private IBeatmapSetInfo getDownloadableBeatmapSet()
{
var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo;
beatmap.Metadata.Artist = "test";
beatmap.Metadata.Title = "undownloadable";
beatmap.Metadata.AuthorString = "test";
var apiBeatmapSet = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo.OnlineInfo;
beatmap.OnlineInfo.HasVideo = true;
beatmap.OnlineInfo.HasStoryboard = true;
apiBeatmapSet.HasVideo = true;
apiBeatmapSet.HasStoryboard = true;
beatmap.OnlineInfo.Availability = new BeatmapSetOnlineAvailability
return apiBeatmapSet;
}
private IBeatmapSetInfo getUndownloadableBeatmapSet()
{
var apiBeatmapSet = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo.OnlineInfo;
apiBeatmapSet.Artist = "test";
apiBeatmapSet.Title = "undownloadable";
apiBeatmapSet.AuthorString = "test";
apiBeatmapSet.HasVideo = true;
apiBeatmapSet.HasStoryboard = true;
apiBeatmapSet.Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
ExternalLink = "http://osu.ppy.sh",
};
return beatmap;
return apiBeatmapSet;
}
private class TestDownloadButton : BeatmapPanelDownloadButton
@@ -146,7 +144,7 @@ namespace osu.Game.Tests.Visual.Online
public DownloadState DownloadState => State.Value;
public TestDownloadButton(BeatmapSetInfo beatmapSet)
public TestDownloadButton(IBeatmapSetInfo beatmapSet)
: base(beatmapSet)
{
}
@@ -18,104 +18,25 @@ namespace osu.Game.Tests.Visual.Online
[Cached(typeof(IPreviewTrackOwner))]
public class TestSceneDirectPanel : OsuTestScene, IPreviewTrackOwner
{
private BeatmapSetInfo getUndownloadableBeatmapSet() => new BeatmapSetInfo
{
OnlineBeatmapSetID = 123,
Metadata = new BeatmapMetadata
{
Title = "undownloadable beatmap",
Artist = "test",
Source = "more tests",
Author = new User
{
Username = "BanchoBot",
Id = 3,
},
},
OnlineInfo = new APIBeatmapSet
{
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
},
Preview = @"https://b.ppy.sh/preview/12345.mp3",
PlayCount = 123,
FavouriteCount = 456,
BPM = 111,
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Ruleset = Ruleset.Value,
Version = "Test",
StarDifficulty = 6.42,
}
}
};
private BeatmapSetInfo getManyDifficultiesBeatmapSet(RulesetStore rulesets)
{
var beatmaps = new List<BeatmapInfo>();
for (int i = 0; i < 100; i++)
{
beatmaps.Add(new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(i % 4),
StarDifficulty = 2 + i % 4 * 2,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = 3.5f,
}
});
}
return new BeatmapSetInfo
{
OnlineBeatmapSetID = 1,
Metadata = new BeatmapMetadata
{
Title = "many difficulties beatmap",
Artist = "test",
Author = new User
{
Username = "BanchoBot",
Id = 3,
}
},
OnlineInfo = new APIBeatmapSet
{
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
},
Beatmaps = beatmaps,
};
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var normal = getBeatmapSet();
normal.OnlineInfo.HasVideo = true;
normal.OnlineInfo.HasStoryboard = true;
normal.HasVideo = true;
normal.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet();
var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets);
var manyDifficulties = getManyDifficultiesBeatmapSet();
var explicitMap = getBeatmapSet();
explicitMap.OnlineInfo.HasExplicitContent = true;
explicitMap.HasExplicitContent = true;
var featuredMap = getBeatmapSet();
featuredMap.OnlineInfo.TrackId = 1;
featuredMap.TrackId = 1;
var explicitFeaturedMap = getBeatmapSet();
explicitFeaturedMap.OnlineInfo.HasExplicitContent = true;
explicitFeaturedMap.OnlineInfo.TrackId = 2;
explicitFeaturedMap.HasExplicitContent = true;
explicitFeaturedMap.TrackId = 2;
Child = new BasicScrollContainer
{
@@ -145,7 +66,72 @@ namespace osu.Game.Tests.Visual.Online
},
};
BeatmapSetInfo getBeatmapSet() => CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
APIBeatmapSet getBeatmapSet() => CreateAPIBeatmapSet(Ruleset.Value);
APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet
{
OnlineID = 123,
Title = "undownloadable beatmap",
Artist = "test",
Source = "more tests",
Author = new User
{
Username = "BanchoBot",
Id = 3,
},
Availability = new BeatmapSetOnlineAvailability
{
DownloadDisabled = true,
},
Preview = @"https://b.ppy.sh/preview/12345.mp3",
PlayCount = 123,
FavouriteCount = 456,
BPM = 111,
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = new List<APIBeatmap>
{
new APIBeatmap
{
RulesetID = Ruleset.Value.ID ?? 0,
DifficultyName = "Test",
StarRating = 6.42,
}
}
};
APIBeatmapSet getManyDifficultiesBeatmapSet()
{
var beatmaps = new List<APIBeatmap>();
for (int i = 0; i < 100; i++)
{
beatmaps.Add(new APIBeatmap
{
RulesetID = i % 4,
StarRating = 2 + i % 4 * 2,
OverallDifficulty = 3.5f,
});
}
return new APIBeatmapSet
{
OnlineID = 1,
Title = "undownloadable beatmap",
Artist = "test",
Source = "more tests",
Author = new User
{
Username = "BanchoBot",
Id = 3,
},
HasVideo = true,
HasStoryboard = true,
Covers = new BeatmapSetOnlineCovers(),
Beatmaps = beatmaps,
};
}
}
}
}
@@ -4,7 +4,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osuTK;
@@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestLoggedOutIn()
{
AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo { OnlineBeatmapSetID = 88 });
AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new APIBeatmapSet { OnlineID = 88 });
AddStep("log out", () => API.Logout());
checkEnabled(false);
AddStep("log in", () => API.Login("test", "test"));
@@ -40,9 +40,9 @@ namespace osu.Game.Tests.Visual.Online
public void TestBeatmapChange()
{
AddStep("log in", () => API.Login("test", "test"));
AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo { OnlineBeatmapSetID = 88 });
AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new APIBeatmapSet { OnlineID = 88 });
checkEnabled(true);
AddStep("set invalid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo());
AddStep("set invalid beatmap", () => favourite.BeatmapSet.Value = new APIBeatmapSet());
checkEnabled(false);
}
@@ -26,7 +26,8 @@ namespace osu.Game.Tests.Visual.Online
{
LeaderboardModSelector modSelector;
FillFlowContainer<SpriteText> selectedMods;
var ruleset = new Bindable<RulesetInfo>();
var ruleset = new Bindable<IRulesetInfo>();
Add(selectedMods = new FillFlowContainer<SpriteText>
{
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.BeatmapSet.Scores;
@@ -43,11 +44,11 @@ namespace osu.Game.Tests.Visual.Online
}
};
var allScores = new APILegacyScores
var allScores = new APIScoresCollection
{
Scores = new List<APILegacyScoreInfo>
Scores = new List<APIScoreInfo>
{
new APILegacyScoreInfo
new APIScoreInfo
{
User = new User
{
@@ -61,10 +62,10 @@ namespace osu.Game.Tests.Visual.Online
},
Mods = new[]
{
new OsuModDoubleTime().Acronym,
new OsuModHidden().Acronym,
new OsuModFlashlight().Acronym,
new OsuModHardRock().Acronym,
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
new APIMod { Acronym = new OsuModHidden().Acronym },
new APIMod { Acronym = new OsuModFlashlight().Acronym },
new APIMod { Acronym = new OsuModHardRock().Acronym },
},
Rank = ScoreRank.XH,
PP = 200,
@@ -72,7 +73,7 @@ namespace osu.Game.Tests.Visual.Online
TotalScore = 1234567890,
Accuracy = 1,
},
new APILegacyScoreInfo
new APIScoreInfo
{
User = new User
{
@@ -86,9 +87,9 @@ namespace osu.Game.Tests.Visual.Online
},
Mods = new[]
{
new OsuModDoubleTime().Acronym,
new OsuModHidden().Acronym,
new OsuModFlashlight().Acronym,
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
new APIMod { Acronym = new OsuModHidden().Acronym },
new APIMod { Acronym = new OsuModFlashlight().Acronym },
},
Rank = ScoreRank.S,
PP = 190,
@@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual.Online
TotalScore = 1234789,
Accuracy = 0.9997,
},
new APILegacyScoreInfo
new APIScoreInfo
{
User = new User
{
@@ -110,8 +111,8 @@ namespace osu.Game.Tests.Visual.Online
},
Mods = new[]
{
new OsuModDoubleTime().Acronym,
new OsuModHidden().Acronym,
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
new APIMod { Acronym = new OsuModHidden().Acronym },
},
Rank = ScoreRank.B,
PP = 180,
@@ -119,7 +120,7 @@ namespace osu.Game.Tests.Visual.Online
TotalScore = 12345678,
Accuracy = 0.9854,
},
new APILegacyScoreInfo
new APIScoreInfo
{
User = new User
{
@@ -133,7 +134,7 @@ namespace osu.Game.Tests.Visual.Online
},
Mods = new[]
{
new OsuModDoubleTime().Acronym,
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
},
Rank = ScoreRank.C,
PP = 170,
@@ -141,7 +142,7 @@ namespace osu.Game.Tests.Visual.Online
TotalScore = 1234567,
Accuracy = 0.8765,
},
new APILegacyScoreInfo
new APIScoreInfo
{
User = new User
{
@@ -162,9 +163,9 @@ namespace osu.Game.Tests.Visual.Online
}
};
var myBestScore = new APILegacyUserTopScoreInfo
var myBestScore = new APIScoreWithPosition
{
Score = new APILegacyScoreInfo
Score = new APIScoreInfo
{
User = new User
{
@@ -185,9 +186,9 @@ namespace osu.Game.Tests.Visual.Online
Position = 1337,
};
var myBestScoreWithNullPosition = new APILegacyUserTopScoreInfo
var myBestScoreWithNullPosition = new APIScoreWithPosition
{
Score = new APILegacyScoreInfo
Score = new APIScoreInfo
{
User = new User
{
@@ -208,11 +209,11 @@ namespace osu.Game.Tests.Visual.Online
Position = null,
};
var oneScore = new APILegacyScores
var oneScore = new APIScoresCollection
{
Scores = new List<APILegacyScoreInfo>
Scores = new List<APIScoreInfo>
{
new APILegacyScoreInfo
new APIScoreInfo
{
User = new User
{
@@ -226,10 +227,10 @@ namespace osu.Game.Tests.Visual.Online
},
Mods = new[]
{
new OsuModDoubleTime().Acronym,
new OsuModHidden().Acronym,
new OsuModFlashlight().Acronym,
new OsuModHardRock().Acronym,
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
new APIMod { Acronym = new OsuModHidden().Acronym },
new APIMod { Acronym = new OsuModFlashlight().Acronym },
new APIMod { Acronym = new OsuModHardRock().Acronym },
},
Rank = ScoreRank.XH,
PP = 200,
@@ -273,7 +274,7 @@ namespace osu.Game.Tests.Visual.Online
private class TestScoresContainer : ScoresContainer
{
public new APILegacyScores Scores
public new APIScoresCollection Scores
{
set => base.Scores = value;
}
@@ -2,16 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Framework.Graphics;
using osu.Game.Scoring;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Overlays;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Tests.Visual.Online
{
@@ -19,79 +19,79 @@ namespace osu.Game.Tests.Visual.Online
{
public TestSceneUserProfileScores()
{
var firstScore = new ScoreInfo
var firstScore = new APIScoreInfo
{
PP = 1047.21,
Rank = ScoreRank.SH,
BeatmapInfo = new BeatmapInfo
Beatmap = new APIBeatmap
{
Metadata = new BeatmapMetadata
BeatmapSet = new APIBeatmapSet
{
Title = "JUSTadICE (TV Size)",
Artist = "Oomori Seiko"
Artist = "Oomori Seiko",
},
Version = "Extreme"
DifficultyName = "Extreme"
},
Date = DateTimeOffset.Now,
Mods = new Mod[]
Mods = new[]
{
new OsuModHidden(),
new OsuModHardRock(),
new OsuModDoubleTime()
new APIMod { Acronym = new OsuModHidden().Acronym },
new APIMod { Acronym = new OsuModHardRock().Acronym },
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
},
Accuracy = 0.9813
};
var secondScore = new ScoreInfo
var secondScore = new APIScoreInfo
{
PP = 134.32,
Rank = ScoreRank.A,
BeatmapInfo = new BeatmapInfo
Beatmap = new APIBeatmap
{
Metadata = new BeatmapMetadata
BeatmapSet = new APIBeatmapSet
{
Title = "Triumph & Regret",
Artist = "typeMARS"
Artist = "typeMARS",
},
Version = "[4K] Regret"
DifficultyName = "[4K] Regret"
},
Date = DateTimeOffset.Now,
Mods = new Mod[]
Mods = new[]
{
new OsuModHardRock(),
new OsuModDoubleTime(),
new APIMod { Acronym = new OsuModHardRock().Acronym },
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
},
Accuracy = 0.998546
};
var thirdScore = new ScoreInfo
var thirdScore = new APIScoreInfo
{
PP = 96.83,
Rank = ScoreRank.S,
BeatmapInfo = new BeatmapInfo
Beatmap = new APIBeatmap
{
Metadata = new BeatmapMetadata
BeatmapSet = new APIBeatmapSet
{
Title = "Idolize",
Artist = "Creo"
Artist = "Creo",
},
Version = "Insane"
DifficultyName = "Insane"
},
Date = DateTimeOffset.Now,
Accuracy = 0.9726
};
var noPPScore = new ScoreInfo
var noPPScore = new APIScoreInfo
{
Rank = ScoreRank.B,
BeatmapInfo = new BeatmapInfo
Beatmap = new APIBeatmap
{
Metadata = new BeatmapMetadata
BeatmapSet = new APIBeatmapSet
{
Title = "C18H27NO3(extend)",
Artist = "Team Grimoire"
Artist = "Team Grimoire",
},
Version = "[4K] Cataclysmic Hypernova"
DifficultyName = "[4K] Cataclysmic Hypernova"
},
Date = DateTimeOffset.Now,
Accuracy = 0.55879
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Playlists
{
public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
{
protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager;
private TestLoungeSubScreen loungeScreen;
@@ -11,24 +11,27 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Online.Rooms;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Screens.OnlinePlay.Playlists;
using osu.Game.Screens.Play;
using osu.Game.Tests.Beatmaps;
using osu.Game.Tests.Visual.OnlinePlay;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Playlists
{
public class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene
public class TestScenePlaylistsRoomCreation : OnlinePlayTestScene
{
private BeatmapManager manager;
private RulesetStore rulesets;
private TestPlaylistsRoomSubScreen match;
private ILive<BeatmapSetInfo> importedBeatmap;
[BackgroundDependencyLoader]
private void load(GameHost host, AudioManager audio)
{
@@ -40,7 +43,9 @@ namespace osu.Game.Tests.Visual.Playlists
public void SetupSteps()
{
AddStep("set room", () => SelectedRoom.Value = new Room());
AddStep("ensure has beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait());
importBeatmap();
AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value)));
AddUntilStep("wait for load", () => match.IsCurrentScreen());
}
@@ -48,44 +53,58 @@ namespace osu.Game.Tests.Visual.Playlists
[Test]
public void TestLoadSimpleMatch()
{
AddStep("set room properties", () =>
setupAndCreateRoom(room =>
{
SelectedRoom.Value.RoomID.Value = 1;
SelectedRoom.Value.Name.Value = "my awesome room";
SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value);
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
SelectedRoom.Value.Playlist.Add(new PlaylistItem
room.Name.Value = "my awesome room";
room.Host.Value = API.LocalUser.Value;
room.RecentParticipants.Add(room.Host.Value);
room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Beatmap = { Value = importedBeatmap.Value.Beatmaps.First() },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
});
});
AddUntilStep("Progress details are hidden", () => match.ChildrenOfType<RoomLocalUserInfo>().FirstOrDefault()?.Parent.Alpha == 0);
AddStep("start match", () => match.ChildrenOfType<PlaylistsReadyButton>().First().TriggerClick());
AddUntilStep("player loader loaded", () => Stack.CurrentScreen is PlayerLoader);
}
[Test]
public void TestPlaylistItemSelectedOnCreate()
public void TestAttemptLimitedMatch()
{
AddStep("set room properties", () =>
setupAndCreateRoom(room =>
{
SelectedRoom.Value.Name.Value = "my awesome room";
SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem
room.Name.Value = "my awesome room";
room.MaxAttempts.Value = 5;
room.Host.Value = API.LocalUser.Value;
room.RecentParticipants.Add(room.Host.Value);
room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Beatmap = { Value = importedBeatmap.Value.Beatmaps.First() },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
});
});
AddStep("move mouse to create button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
});
AddUntilStep("Progress details are visible", () => match.ChildrenOfType<RoomLocalUserInfo>().FirstOrDefault()?.Parent.Alpha == 1);
}
AddStep("click", () => InputManager.Click(MouseButton.Left));
[Test]
public void TestPlaylistItemSelectedOnCreate()
{
setupAndCreateRoom(room =>
{
room.Name.Value = "my awesome room";
room.Host.Value = API.LocalUser.Value;
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedBeatmap.Value.Beatmaps.First() },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
});
});
AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]);
}
@@ -94,56 +113,51 @@ namespace osu.Game.Tests.Visual.Playlists
public void TestBeatmapUpdatedOnReImport()
{
BeatmapSetInfo importedSet = null;
TestBeatmap beatmap = null;
// this step is required to make sure the further imports actually get online IDs.
// all the playlist logic relies on online ID matching.
AddStep("remove all matching online IDs", () =>
{
beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo);
var existing = manager.QueryBeatmapSets(s => s.OnlineBeatmapSetID == beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID).ToList();
foreach (var s in existing)
{
s.OnlineBeatmapSetID = null;
foreach (var b in s.Beatmaps)
b.OnlineBeatmapID = null;
manager.Update(s);
}
});
AddStep("import altered beatmap", () =>
{
IBeatmap beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo);
beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1;
// intentionally increment online IDs to clash with import below.
beatmap.BeatmapInfo.OnlineBeatmapID++;
beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID++;
importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value;
});
AddStep("load room", () =>
setupAndCreateRoom(room =>
{
SelectedRoom.Value.Name.Value = "my awesome room";
SelectedRoom.Value.Host.Value = API.LocalUser.Value;
SelectedRoom.Value.Playlist.Add(new PlaylistItem
room.Name.Value = "my awesome room";
room.Host.Value = API.LocalUser.Value;
room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = importedSet.Beatmaps[0] },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
});
});
AddStep("create room", () =>
{
InputManager.MoveMouseTo(match.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
InputManager.Click(MouseButton.Left);
});
AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize == 1);
AddStep("re-import original beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait());
importBeatmap();
AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize != 1);
}
private void setupAndCreateRoom(Action<Room> room)
{
AddStep("setup room", () => room(SelectedRoom.Value));
AddStep("click create button", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
InputManager.Click(MouseButton.Left);
});
}
private void importBeatmap() => AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Result);
private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen
{
public new Bindable<PlaylistItem> SelectedItem => base.SelectedItem;
@@ -6,6 +6,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osuTK;
namespace osu.Game.Tests.Visual.Settings
@@ -58,6 +59,13 @@ namespace osu.Game.Tests.Visual.Settings
Default = string.Empty,
Value = "Sample text"
};
[SettingSource("Sample number textbox", "Textbox number entry", SettingControlType = typeof(SettingsNumberBox))]
public Bindable<int?> IntTextboxBindable { get; } = new Bindable<int?>
{
Default = null,
Value = null
};
}
private enum TestEnum
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
@@ -88,7 +89,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select EZ mod", () =>
{
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
SelectedMods.Value = new[] { ruleset.CreateMod<ModEasy>() };
});
@@ -105,7 +106,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select HR mod", () =>
{
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
SelectedMods.Value = new[] { ruleset.CreateMod<ModHardRock>() };
});
@@ -122,9 +123,9 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select unchanged Difficulty Adjust mod", () =>
{
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
var difficultyAdjustMod = ruleset.CreateMod<ModDifficultyAdjust>();
difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.BaseDifficulty);
difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty);
SelectedMods.Value = new[] { difficultyAdjustMod };
});
@@ -141,9 +142,9 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select changed Difficulty Adjust mod", () =>
{
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance();
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
var difficultyAdjustMod = ruleset.CreateMod<OsuModDifficultyAdjust>();
var originalDifficulty = advancedStats.BeatmapInfo.BaseDifficulty;
var originalDifficulty = advancedStats.BeatmapInfo.Difficulty;
difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);
difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f;
@@ -15,6 +15,7 @@ using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
@@ -684,6 +685,7 @@ namespace osu.Game.Tests.Visual.SongSelect
set.Beatmaps.Add(new BeatmapInfo
{
Version = $"Stars: {i}",
Ruleset = new OsuRuleset().RulesetInfo,
StarDifficulty = i,
});
}
@@ -868,6 +870,7 @@ namespace osu.Game.Tests.Visual.SongSelect
OnlineBeatmapID = id++ * 10,
Version = version,
StarDifficulty = diff,
Ruleset = new OsuRuleset().RulesetInfo,
BaseDifficulty = new BeatmapDifficulty
{
OverallDifficulty = diff,
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual.SongSelect
@@ -54,6 +55,7 @@ namespace osu.Game.Tests.Visual.SongSelect
ApproachRate = 3.5f,
},
StarDifficulty = 5.3f,
Ruleset = new OsuRuleset().RulesetInfo,
OnlineInfo = new APIBeatmap
{
FailTimes = new APIFailTimes
@@ -90,6 +92,7 @@ namespace osu.Game.Tests.Visual.SongSelect
ApproachRate = 3.5f,
},
StarDifficulty = 5.3f,
Ruleset = new OsuRuleset().RulesetInfo,
OnlineInfo = new APIBeatmap
{
FailTimes = new APIFailTimes
@@ -119,6 +122,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Source = "osu!",
Tags = "this beatmap has ratings metrics but not retries or fails",
},
Ruleset = new OsuRuleset().RulesetInfo,
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 6,
@@ -148,6 +152,7 @@ namespace osu.Game.Tests.Visual.SongSelect
OverallDifficulty = 6,
ApproachRate = 7,
},
Ruleset = new OsuRuleset().RulesetInfo,
StarDifficulty = 2.91f,
OnlineInfo = new APIBeatmap
{
@@ -171,6 +176,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Source = "osu!",
Tags = "this beatmap has no metrics",
},
Ruleset = new OsuRuleset().RulesetInfo,
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 5,
@@ -194,6 +200,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("online ratings/retries/fails", () => details.BeatmapInfo = new BeatmapInfo
{
OnlineBeatmapID = 162,
Ruleset = new OsuRuleset().RulesetInfo
});
AddStep("set online", () => api.SetState(APIState.Online));
AddStep("set offline", () => api.SetState(APIState.Offline));
@@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}
}
public override async Task<StarDifficulty> GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
public override async Task<StarDifficulty> GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo rulesetInfo = null, IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
{
if (blockCalculation)
await calculationBlocker.Task.ConfigureAwait(false);
@@ -0,0 +1,83 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens;
using osu.Game.Screens.OnlinePlay.Components;
using osu.Game.Tests.Visual.Multiplayer;
using osu.Game.Tests.Visual.OnlinePlay;
namespace osu.Game.Tests.Visual
{
/// <summary>
/// An <see cref="OsuScreen"/> loadable into <see cref="ScreenTestScene"/>s via <see cref="ScreenTestScene.LoadScreen"/>,
/// which provides dependencies for and loads an isolated <see cref="Screens.OnlinePlay.Multiplayer.Multiplayer"/> screen.
/// <p>
/// This screen:
/// <list type="bullet">
/// <item>Provides a <see cref="TestMultiplayerClient"/> to be resolved as a dependency in the <see cref="Screens.OnlinePlay.Multiplayer.Multiplayer"/> screen,
/// which is typically a part of <see cref="OsuGameBase"/>.</item>
/// <item>Rebinds the <see cref="DummyAPIAccess"/> to handle requests via a <see cref="TestRoomRequestsHandler"/>.</item>
/// <item>Provides a <see cref="TestMultiplayerRoomManager"/> for the <see cref="Screens.OnlinePlay.Multiplayer.Multiplayer"/> screen.</item>
/// </list>
/// </p>
/// </summary>
public class TestMultiplayerScreenStack : OsuScreen
{
public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen => multiplayerScreen;
public TestMultiplayerRoomManager RoomManager => multiplayerScreen.RoomManager;
public IScreen CurrentScreen => screenStack.CurrentScreen;
public new bool IsLoaded => base.IsLoaded && MultiplayerScreen.IsLoaded;
[Cached(typeof(MultiplayerClient))]
public readonly TestMultiplayerClient Client;
private readonly OsuScreenStack screenStack;
private readonly TestMultiplayer multiplayerScreen;
public TestMultiplayerScreenStack()
{
multiplayerScreen = new TestMultiplayer();
InternalChildren = new Drawable[]
{
Client = new TestMultiplayerClient(RoomManager),
screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }
};
screenStack.Push(multiplayerScreen);
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api, OsuGameBase game)
{
((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, game);
}
public override bool OnBackButton() => multiplayerScreen.OnBackButton();
public override bool OnExiting(IScreen next)
{
if (screenStack.CurrentScreen == null)
return base.OnExiting(next);
screenStack.Exit();
return true;
}
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
{
public new TestMultiplayerRoomManager RoomManager { get; private set; }
public TestRoomRequestsHandler RequestsHandler { get; private set; }
protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(RequestsHandler = new TestRoomRequestsHandler());
}
}
}
@@ -110,25 +110,19 @@ namespace osu.Game.Tests.Visual.UserInterface
base.Dispose(isDisposing);
}
private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo
private static readonly APIBeatmapSet beatmap_set = new APIBeatmapSet
{
OnlineInfo = new APIBeatmapSet
Covers = new BeatmapSetOnlineCovers
{
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1094296/covers/cover@2x.jpg?1581416305"
}
Cover = "https://assets.ppy.sh/beatmaps/1094296/covers/cover@2x.jpg?1581416305"
}
};
private static readonly BeatmapSetInfo no_cover_beatmap_set = new BeatmapSetInfo
private static readonly APIBeatmapSet no_cover_beatmap_set = new APIBeatmapSet
{
OnlineInfo = new APIBeatmapSet
Covers = new BeatmapSetOnlineCovers
{
Covers = new BeatmapSetOnlineCovers
{
Cover = string.Empty
}
Cover = string.Empty
}
};
}
@@ -56,95 +56,71 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("Set width to 300", () => content.ResizeWidthTo(300, 500));
}
private static readonly List<BeatmapSetInfo> new_beatmaps = new List<BeatmapSetInfo>
private static readonly List<APIBeatmapSet> new_beatmaps = new List<APIBeatmapSet>
{
new BeatmapSetInfo
new APIBeatmapSet
{
Metadata = new BeatmapMetadata
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new User
{
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new User
{
Username = "author",
Id = 100
}
Username = "author",
Id = 100
},
OnlineInfo = new APIBeatmapSet
Covers = new BeatmapSetOnlineCovers
{
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
}
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
},
new BeatmapSetInfo
new APIBeatmapSet
{
Metadata = new BeatmapMetadata
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new User
{
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new User
{
Username = "author",
Id = 100
}
Username = "author",
Id = 100
},
OnlineInfo = new APIBeatmapSet
Covers = new BeatmapSetOnlineCovers
{
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.MinValue
}
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
}
};
private static readonly List<BeatmapSetInfo> popular_beatmaps = new List<BeatmapSetInfo>
private static readonly List<APIBeatmapSet> popular_beatmaps = new List<APIBeatmapSet>
{
new BeatmapSetInfo
new APIBeatmapSet
{
Metadata = new BeatmapMetadata
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new User
{
Title = "Title",
Artist = "Artist",
Author = new User
{
Username = "author",
Id = 100
}
Username = "author",
Id = 100
},
OnlineInfo = new APIBeatmapSet
Covers = new BeatmapSetOnlineCovers
{
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586",
},
FavouriteCount = 100
}
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
},
new BeatmapSetInfo
new APIBeatmapSet
{
Metadata = new BeatmapMetadata
Title = "Very Long Title (TV size) [TATOE]",
Artist = "This artist has a really long name how is this possible",
Author = new User
{
Title = "Title 2",
Artist = "Artist 2",
Author = new User
{
Username = "someone",
Id = 100
}
Username = "author",
Id = 100
},
OnlineInfo = new APIBeatmapSet
Covers = new BeatmapSetOnlineCovers
{
Covers = new BeatmapSetOnlineCovers
{
Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586",
},
FavouriteCount = 10
}
Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608",
},
Ranked = DateTimeOffset.Now
}
};
}
@@ -41,6 +41,44 @@ namespace osu.Game.Tests.Visual.UserInterface
notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
});
[Test]
public void TestCompleteProgress()
{
ProgressNotification notification = null;
AddStep("add progress notification", () =>
{
notification = new ProgressNotification
{
Text = @"Uploading to BSS...",
CompletionText = "Uploaded to BSS!",
};
notificationOverlay.Post(notification);
progressingNotifications.Add(notification);
});
AddUntilStep("wait completion", () => notification.State == ProgressNotificationState.Completed);
}
[Test]
public void TestCancelProgress()
{
ProgressNotification notification = null;
AddStep("add progress notification", () =>
{
notification = new ProgressNotification
{
Text = @"Uploading to BSS...",
CompletionText = "Uploaded to BSS!",
};
notificationOverlay.Post(notification);
progressingNotifications.Add(notification);
});
AddWaitStep("wait 3", 3);
AddStep("cancel notification", () => notification.State = ProgressNotificationState.Cancelled);
}
[Test]
public void TestBasicFlow()
{
@@ -138,7 +176,7 @@ namespace osu.Game.Tests.Visual.UserInterface
foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active))
{
if (n.Progress < 1)
n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle();
n.Progress += (float)(Time.Elapsed / 2000);
else
n.State = ProgressNotificationState.Completed;
}
@@ -12,6 +12,7 @@ using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
@@ -24,7 +25,6 @@ namespace osu.Game.Tests.Visual.UserInterface
private BeatmapSetInfo testBeatmap;
private IAPIProvider api;
private RulesetStore rulesets;
[Resolved]
private BeatmapManager beatmaps { get; set; }
@@ -33,7 +33,6 @@ namespace osu.Game.Tests.Visual.UserInterface
private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets)
{
this.api = api;
this.rulesets = rulesets;
testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result;
}
@@ -81,7 +80,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Child = background = new TestUpdateableBeatmapBackgroundSprite
{
RelativeSizeAxes = Axes.Both,
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Response?.ToBeatmapSet(rulesets) } }
Beatmap = { Value = new APIBeatmap { BeatmapSet = req.Response } }
};
});
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddStep("setup cover", () => Child = new UpdateableOnlineBeatmapSetCover(coverType)
{
BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet,
OnlineInfo = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet.OnlineInfo,
RelativeSizeAxes = Axes.Both,
Masking = true,
});
@@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.UserInterface
var cover = new UpdateableOnlineBeatmapSetCover(coverType)
{
BeatmapSet = setInfo,
OnlineInfo = setInfo.OnlineInfo,
Height = 100,
Masking = true,
};
@@ -99,12 +99,12 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover
{
BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet,
OnlineInfo = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet.OnlineInfo,
RelativeSizeAxes = Axes.Both,
Masking = true,
});
AddStep("change model", () => updateableCover.BeatmapSet = null);
AddStep("change model", () => updateableCover.OnlineInfo = null);
AddWaitStep("wait some", 5);
AddAssert("no cover added", () => !updateableCover.ChildrenOfType<DelayedLoadUnloadWrapper>().Any());
}
@@ -117,7 +117,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover(0)
{
BeatmapSet = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg"),
OnlineInfo = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg").OnlineInfo,
RelativeSizeAxes = Axes.Both,
Masking = true,
Alpha = 0.4f
@@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddUntilStep("wait for fade complete", () => initialCover.Alpha == 1);
AddStep("switch beatmap",
() => updateableCover.BeatmapSet = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg"));
() => updateableCover.OnlineInfo = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg").OnlineInfo);
AddUntilStep("new cover loaded", () => updateableCover.ChildrenOfType<OnlineBeatmapSetCover>().Except(new[] { initialCover }).Any());
}
+1 -1
View File
@@ -3,7 +3,7 @@
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
@@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
@@ -23,14 +22,13 @@ namespace osu.Game.Tournament.Tests.Components
[BackgroundDependencyLoader]
private void load()
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 });
var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = 1091460 });
req.Success += success;
api.Queue(req);
}
private void success(APIBeatmap apiBeatmap)
private void success(APIBeatmap beatmap)
{
var beatmap = apiBeatmap.ToBeatmapInfo(rulesets);
Add(new TournamentBeatmapPanel(beatmap)
{
Anchor = Anchor.Centre,
@@ -4,12 +4,12 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Tournament.Components;
using osuTK;
namespace osu.Game.Tournament.Tests.Components
{
@@ -23,12 +23,10 @@ namespace osu.Game.Tournament.Tests.Components
private FillFlowContainer<TournamentBeatmapPanel> fillFlow;
private BeatmapInfo beatmapInfo;
[BackgroundDependencyLoader]
private void load()
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 490154 });
var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = 490154 });
req.Success += success;
api.Queue(req);
@@ -38,18 +36,17 @@ namespace osu.Game.Tournament.Tests.Components
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Full,
Spacing = new osuTK.Vector2(10)
Spacing = new Vector2(10)
});
}
private void success(APIBeatmap apiBeatmap)
private void success(APIBeatmap beatmap)
{
beatmapInfo = apiBeatmap.ToBeatmapInfo(rulesets);
var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods;
foreach (var mod in mods)
{
fillFlow.Add(new TournamentBeatmapPanel(beatmapInfo, mod.Acronym)
fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
@@ -44,8 +44,8 @@ namespace osu.Game.Tournament.Tests.NonVisual
{
Beatmaps =
{
new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmapInfo() },
new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmapInfo() },
new RoundBeatmap { Beatmap = TournamentTestScene.CreateSampleBeatmap() },
new RoundBeatmap { Beatmap = TournamentTestScene.CreateSampleBeatmap() },
}
}
},
@@ -132,7 +132,7 @@ namespace osu.Game.Tournament.Tests.Screens
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Mods = mods
});
}
@@ -7,7 +7,6 @@ using osu.Framework.Allocation;
using osu.Framework.Platform;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Tests.Visual;
@@ -74,19 +73,19 @@ namespace osu.Game.Tournament.Tests
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Score = 1234567,
Seed = { Value = 16 },
}
@@ -100,19 +99,19 @@ namespace osu.Game.Tournament.Tests
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Beatmap = CreateSampleBeatmap(),
Score = 234567,
Seed = { Value = 12 },
}
@@ -152,16 +151,15 @@ namespace osu.Game.Tournament.Tests
}
};
public static BeatmapInfo CreateSampleBeatmapInfo() =>
new BeatmapInfo
public static APIBeatmap CreateSampleBeatmap() =>
new APIBeatmap
{
Metadata = new BeatmapMetadata
BeatmapSet = new APIBeatmapSet
{
Title = "Test Title",
Artist = "Test Artist",
ID = RNG.Next(0, 1000000)
},
OnlineInfo = new APIBeatmap(),
OnlineID = RNG.Next(0, 1000000),
};
protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner();
@@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
</ItemGroup>
+16 -16
View File
@@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Screens.Menu;
using osuTK;
@@ -21,22 +22,21 @@ namespace osu.Game.Tournament.Components
{
public class SongBar : CompositeDrawable
{
private BeatmapInfo beatmapInfo;
private APIBeatmap beatmap;
public const float HEIGHT = 145 / 2f;
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; }
public BeatmapInfo BeatmapInfo
public APIBeatmap Beatmap
{
get => beatmapInfo;
set
{
if (beatmapInfo == value)
if (beatmap == value)
return;
beatmapInfo = value;
beatmap = value;
update();
}
}
@@ -95,18 +95,18 @@ namespace osu.Game.Tournament.Components
private void update()
{
if (beatmapInfo == null)
if (beatmap == null)
{
flow.Clear();
return;
}
double bpm = beatmapInfo.BeatmapSet.OnlineInfo.BPM;
double length = beatmapInfo.Length;
double bpm = beatmap.BPM;
double length = beatmap.Length;
string hardRockExtra = "";
string srExtra = "";
float ar = beatmapInfo.BaseDifficulty.ApproachRate;
float ar = beatmap.Difficulty.ApproachRate;
if ((mods & LegacyMods.HardRock) > 0)
{
@@ -132,9 +132,9 @@ namespace osu.Game.Tournament.Components
default:
stats = new (string heading, string content)[]
{
("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
("CS", $"{beatmap.Difficulty.CircleSize:0.#}{hardRockExtra}"),
("AR", $"{ar:0.#}{hardRockExtra}"),
("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
("OD", $"{beatmap.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"),
};
break;
@@ -142,15 +142,15 @@ namespace osu.Game.Tournament.Components
case 3:
stats = new (string heading, string content)[]
{
("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"),
("HP", $"{beatmapInfo.BaseDifficulty.DrainRate:0.#}{hardRockExtra}")
("OD", $"{beatmap.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"),
("HP", $"{beatmap.Difficulty.DrainRate:0.#}{hardRockExtra}")
};
break;
case 2:
stats = new (string heading, string content)[]
{
("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"),
("CS", $"{beatmap.Difficulty.CircleSize:0.#}{hardRockExtra}"),
("AR", $"{ar:0.#}"),
};
break;
@@ -186,7 +186,7 @@ namespace osu.Game.Tournament.Components
Children = new Drawable[]
{
new DiffPiece(stats),
new DiffPiece(("Star Rating", $"{beatmapInfo.StarDifficulty:0.#}{srExtra}"))
new DiffPiece(("Star Rating", $"{beatmap.StarRating:0.#}{srExtra}"))
}
},
new FillFlowContainer
@@ -229,7 +229,7 @@ namespace osu.Game.Tournament.Components
}
}
},
new TournamentBeatmapPanel(beatmapInfo)
new TournamentBeatmapPanel(beatmap)
{
RelativeSizeAxes = Axes.X,
Width = 0.5f,
@@ -13,6 +13,7 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tournament.Models;
using osuTK.Graphics;
@@ -20,7 +21,7 @@ namespace osu.Game.Tournament.Components
{
public class TournamentBeatmapPanel : CompositeDrawable
{
public readonly IBeatmapInfo BeatmapInfo;
public readonly APIBeatmap Beatmap;
private readonly string mod;
@@ -32,11 +33,11 @@ namespace osu.Game.Tournament.Components
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
private Box flash;
public TournamentBeatmapPanel(IBeatmapInfo beatmapInfo, string mod = null)
public TournamentBeatmapPanel(APIBeatmap beatmap, string mod = null)
{
if (beatmapInfo == null) throw new ArgumentNullException(nameof(beatmapInfo));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
BeatmapInfo = beatmapInfo;
Beatmap = beatmap;
this.mod = mod;
Width = 400;
@@ -62,7 +63,7 @@ namespace osu.Game.Tournament.Components
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.5f),
BeatmapSet = BeatmapInfo.BeatmapSet as IBeatmapSetOnlineInfo,
OnlineInfo = Beatmap.BeatmapSet,
},
new FillFlowContainer
{
@@ -75,7 +76,7 @@ namespace osu.Game.Tournament.Components
{
new TournamentSpriteText
{
Text = BeatmapInfo.GetDisplayTitleRomanisable(false),
Text = Beatmap.GetDisplayTitleRomanisable(false, false),
Font = OsuFont.Torus.With(weight: FontWeight.Bold),
},
new FillFlowContainer
@@ -92,7 +93,7 @@ namespace osu.Game.Tournament.Components
},
new TournamentSpriteText
{
Text = BeatmapInfo.Metadata?.Author,
Text = Beatmap.Metadata.Author,
Padding = new MarginPadding { Right = 20 },
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
},
@@ -104,7 +105,7 @@ namespace osu.Game.Tournament.Components
},
new TournamentSpriteText
{
Text = BeatmapInfo.DifficultyName,
Text = Beatmap.DifficultyName,
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
},
}
@@ -148,7 +149,7 @@ namespace osu.Game.Tournament.Components
private void updateState()
{
var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == BeatmapInfo.OnlineID);
var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineID);
bool doFlash = found != choice;
choice = found;
+5 -5
View File
@@ -11,10 +11,10 @@ using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets;
using osu.Game.Tournament.Models;
@@ -87,14 +87,14 @@ namespace osu.Game.Tournament.IPC
lastBeatmapId = beatmapId;
var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null);
var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.Beatmap != null);
if (existing != null)
Beatmap.Value = existing.BeatmapInfo;
Beatmap.Value = existing.Beatmap;
else
{
beatmapLookupRequest = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId });
beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmapInfo(Rulesets);
beatmapLookupRequest = new GetBeatmapRequest(new APIBeatmap { OnlineID = beatmapId });
beatmapLookupRequest.Success += b => Beatmap.Value = b;
API.Queue(beatmapLookupRequest);
}
}
+2 -2
View File
@@ -3,14 +3,14 @@
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Tournament.IPC
{
public class MatchIPCInfo : Component
{
public Bindable<BeatmapInfo> Beatmap { get; } = new Bindable<BeatmapInfo>();
public Bindable<APIBeatmap> Beatmap { get; } = new Bindable<APIBeatmap>();
public Bindable<LegacyMods> Mods { get; } = new Bindable<LegacyMods>();
public Bindable<TourneyState> State { get; } = new Bindable<TourneyState>();
public Bindable<string> ChatChannel { get; } = new Bindable<string>();
+4 -2
View File
@@ -1,7 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Tournament.Models
{
@@ -10,6 +11,7 @@ namespace osu.Game.Tournament.Models
public int ID;
public string Mods;
public BeatmapInfo BeatmapInfo;
[JsonProperty("BeatmapInfo")]
public APIBeatmap Beatmap;
}
}
+4 -2
View File
@@ -1,8 +1,9 @@
// 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 Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Tournament.Models
{
@@ -10,7 +11,8 @@ namespace osu.Game.Tournament.Models
{
public int ID;
public BeatmapInfo BeatmapInfo;
[JsonProperty("BeatmapInfo")]
public APIBeatmap Beatmap;
public long Score;
@@ -4,8 +4,8 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC;
@@ -37,10 +37,10 @@ namespace osu.Game.Tournament.Screens
SongBar.Mods = mods.NewValue;
}
private void beatmapChanged(ValueChangedEvent<BeatmapInfo> beatmap)
private void beatmapChanged(ValueChangedEvent<APIBeatmap> beatmap)
{
SongBar.FadeInFromZero(300, Easing.OutQuint);
SongBar.BeatmapInfo = beatmap.NewValue;
SongBar.Beatmap = beatmap.NewValue;
}
}
}
@@ -7,10 +7,10 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using osu.Game.Tournament.Components;
@@ -226,25 +226,25 @@ namespace osu.Game.Tournament.Screens.Editors
Model.ID = id.NewValue ?? 0;
if (id.NewValue != id.OldValue)
Model.BeatmapInfo = null;
Model.Beatmap = null;
if (Model.BeatmapInfo != null)
if (Model.Beatmap != null)
{
updatePanel();
return;
}
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = Model.ID });
var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = Model.ID });
req.Success += res =>
{
Model.BeatmapInfo = res.ToBeatmapInfo(rulesets);
Model.Beatmap = res;
updatePanel();
};
req.Failure += _ =>
{
Model.BeatmapInfo = null;
Model.Beatmap = null;
updatePanel();
};
@@ -259,9 +259,9 @@ namespace osu.Game.Tournament.Screens.Editors
{
drawableContainer.Clear();
if (Model.BeatmapInfo != null)
if (Model.Beatmap != null)
{
drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, Model.Mods)
drawableContainer.Child = new TournamentBeatmapPanel(Model.Beatmap, Model.Mods)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@@ -7,10 +7,10 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using osu.Game.Tournament.Components;
@@ -234,25 +234,25 @@ namespace osu.Game.Tournament.Screens.Editors
Model.ID = id.NewValue ?? 0;
if (id.NewValue != id.OldValue)
Model.BeatmapInfo = null;
Model.Beatmap = null;
if (Model.BeatmapInfo != null)
if (Model.Beatmap != null)
{
updatePanel();
return;
}
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = Model.ID });
var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = Model.ID });
req.Success += res =>
{
Model.BeatmapInfo = res.ToBeatmapInfo(rulesets);
Model.Beatmap = res;
updatePanel();
};
req.Failure += _ =>
{
Model.BeatmapInfo = null;
Model.Beatmap = null;
updatePanel();
};
@@ -267,9 +267,9 @@ namespace osu.Game.Tournament.Screens.Editors
{
drawableContainer.Clear();
if (Model.BeatmapInfo != null)
if (Model.Beatmap != null)
{
drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, result.Mod.Value)
drawableContainer.Child = new TournamentBeatmapPanel(Model.Beatmap, result.Mod.Value)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@@ -8,8 +8,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
@@ -105,14 +105,14 @@ namespace osu.Game.Tournament.Screens.MapPool
ipc.Beatmap.BindValueChanged(beatmapChanged);
}
private void beatmapChanged(ValueChangedEvent<BeatmapInfo> beatmap)
private void beatmapChanged(ValueChangedEvent<APIBeatmap> beatmap)
{
if (CurrentMatch.Value == null || CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2)
return;
// if bans have already been placed, beatmap changes result in a selection being made autoamtically
if (beatmap.NewValue.OnlineBeatmapID != null)
addForBeatmap(beatmap.NewValue.OnlineBeatmapID.Value);
if (beatmap.NewValue.OnlineID > 0)
addForBeatmap(beatmap.NewValue.OnlineID);
}
private void setMode(TeamColour colour, ChoiceType choiceType)
@@ -147,11 +147,11 @@ namespace osu.Game.Tournament.Screens.MapPool
if (map != null)
{
if (e.Button == MouseButton.Left && map.BeatmapInfo.OnlineID > 0)
addForBeatmap(map.BeatmapInfo.OnlineID);
if (e.Button == MouseButton.Left && map.Beatmap.OnlineID > 0)
addForBeatmap(map.Beatmap.OnlineID);
else
{
var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.BeatmapInfo.OnlineID);
var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineID);
if (existing != null)
{
@@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Screens.MapPool
if (CurrentMatch.Value == null)
return;
if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId))
if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.Beatmap.OnlineID != beatmapId))
// don't attempt to add if the beatmap isn't in our pool
return;
@@ -245,7 +245,7 @@ namespace osu.Game.Tournament.Screens.MapPool
flowCount = 1;
}
currentFlow.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods)
currentFlow.Add(new TournamentBeatmapPanel(b.Beatmap, b.Mods)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@@ -141,9 +141,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro
Spacing = new Vector2(5),
Children = new Drawable[]
{
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = TournamentGame.TEXT_COLOUR, },
new TournamentSpriteText { Text = beatmap.Beatmap.Metadata.Title, Colour = TournamentGame.TEXT_COLOUR, },
new TournamentSpriteText { Text = "by", Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
new TournamentSpriteText { Text = beatmap.Beatmap.Metadata.Artist, Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) },
}
},
new FillFlowContainer
+107 -76
View File
@@ -7,12 +7,14 @@ using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Tournament.IO;
using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models;
@@ -39,9 +41,18 @@ namespace osu.Game.Tournament
return dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
}
private TournamentSpriteText initialisationText;
[BackgroundDependencyLoader]
private void load(Storage baseStorage)
{
AddInternal(initialisationText = new TournamentSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.Torus.With(size: 32),
});
Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly));
dependencies.CacheAs<Storage>(storage = new TournamentStorage(baseStorage));
@@ -123,7 +134,8 @@ namespace osu.Game.Tournament
}
addedInfo |= addPlayers();
addedInfo |= addBeatmaps();
addedInfo |= addRoundBeatmaps();
addedInfo |= addSeedingBeatmaps();
if (addedInfo)
SaveChanges();
@@ -145,6 +157,8 @@ namespace osu.Game.Tournament
Add(ipc);
taskCompletionSource.SetResult(true);
initialisationText.Expire();
});
}
@@ -153,81 +167,108 @@ namespace osu.Game.Tournament
/// </summary>
private bool addPlayers()
{
bool addedInfo = false;
var playersRequiringPopulation = ladder.Teams
.SelectMany(t => t.Players)
.Where(p => string.IsNullOrEmpty(p.Username)
|| p.Statistics?.GlobalRank == null
|| p.Statistics?.CountryRank == null).ToList();
foreach (var t in ladder.Teams)
if (playersRequiringPopulation.Count == 0)
return false;
for (int i = 0; i < playersRequiringPopulation.Count; i++)
{
foreach (var p in t.Players)
{
if (string.IsNullOrEmpty(p.Username)
|| p.Statistics?.GlobalRank == null
|| p.Statistics?.CountryRank == null)
{
PopulateUser(p, immediate: true);
addedInfo = true;
}
}
var p = playersRequiringPopulation[i];
PopulateUser(p, immediate: true);
updateLoadProgressMessage($"Populating user stats ({i} / {playersRequiringPopulation.Count})");
}
return addedInfo;
return true;
}
/// <summary>
/// Add missing beatmap info based on beatmap IDs
/// </summary>
private bool addBeatmaps()
private bool addRoundBeatmaps()
{
bool addedInfo = false;
var beatmapsRequiringPopulation = ladder.Rounds
.SelectMany(r => r.Beatmaps)
.Where(b => string.IsNullOrEmpty(b.Beatmap?.BeatmapSet?.Title) && b.ID > 0).ToList();
foreach (var r in ladder.Rounds)
if (beatmapsRequiringPopulation.Count == 0)
return false;
for (int i = 0; i < beatmapsRequiringPopulation.Count; i++)
{
foreach (var b in r.Beatmaps.ToList())
{
if (b.BeatmapInfo != null)
continue;
var b = beatmapsRequiringPopulation[i];
if (b.ID > 0)
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
API.Perform(req);
b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = b.ID });
API.Perform(req);
b.Beatmap = req.Response ?? new APIBeatmap();
addedInfo = true;
}
if (b.BeatmapInfo == null)
// if online population couldn't be performed, ensure we don't leave a null value behind
r.Beatmaps.Remove(b);
}
updateLoadProgressMessage($"Populating round beatmaps ({i} / {beatmapsRequiringPopulation.Count})");
}
foreach (var t in ladder.Teams)
{
foreach (var s in t.SeedingResults)
{
foreach (var b in s.Beatmaps)
{
if (b.BeatmapInfo == null && b.ID > 0)
{
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
req.Perform(API);
b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
addedInfo = true;
}
}
}
}
return addedInfo;
return true;
}
/// <summary>
/// Add missing beatmap info based on beatmap IDs
/// </summary>
private bool addSeedingBeatmaps()
{
var beatmapsRequiringPopulation = ladder.Teams
.SelectMany(r => r.SeedingResults)
.SelectMany(r => r.Beatmaps)
.Where(b => string.IsNullOrEmpty(b.Beatmap?.BeatmapSet?.Title) && b.ID > 0).ToList();
if (beatmapsRequiringPopulation.Count == 0)
return false;
for (int i = 0; i < beatmapsRequiringPopulation.Count; i++)
{
var b = beatmapsRequiringPopulation[i];
var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = b.ID });
API.Perform(req);
b.Beatmap = req.Response ?? new APIBeatmap();
updateLoadProgressMessage($"Populating seeding beatmaps ({i} / {beatmapsRequiringPopulation.Count})");
}
return true;
}
private void updateLoadProgressMessage(string s) => Schedule(() => initialisationText.Text = s);
public void PopulateUser(User user, Action success = null, Action failure = null, bool immediate = false)
{
var req = new GetUserRequest(user.Id, Ruleset.Value);
req.Success += res =>
if (immediate)
{
API.Perform(req);
populate();
}
else
{
req.Success += res => { populate(); };
req.Failure += _ =>
{
user.Id = 1;
failure?.Invoke();
};
API.Queue(req);
}
void populate()
{
var res = req.Response;
if (res == null)
return;
user.Id = res.Id;
user.Username = res.Username;
@@ -236,18 +277,7 @@ namespace osu.Game.Tournament
user.Cover = res.Cover;
success?.Invoke();
};
req.Failure += _ =>
{
user.Id = 1;
failure?.Invoke();
};
if (immediate)
API.Perform(req);
else
API.Queue(req);
}
}
protected override void LoadComplete()
@@ -269,18 +299,19 @@ namespace osu.Game.Tournament
ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true)))
.ToList();
// Serialise before opening stream for writing, so if there's a failure it will leave the file in the previous state.
string serialisedLadder = JsonConvert.SerializeObject(ladder,
new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
Converters = new JsonConverter[] { new JsonPointConverter() }
});
using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create))
using (var sw = new StreamWriter(stream))
{
sw.Write(JsonConvert.SerializeObject(ladder,
new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
Converters = new JsonConverter[] { new JsonPointConverter() }
}));
}
sw.Write(serialisedLadder);
}
protected override UserInputManager CreateUserInputManager() => new TournamentInputManager();
+25 -22
View File
@@ -88,7 +88,7 @@ namespace osu.Game.Beatmaps
/// <param name="beatmapInfo">The <see cref="BeatmapInfo"/> to get the difficulty of.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="BeatmapInfo"/>.</param>
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated).</returns>
public IBindable<StarDifficulty?> GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, CancellationToken cancellationToken = default)
public IBindable<StarDifficulty?> GetBindableDifficulty([NotNull] IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default)
{
var bindable = createBindable(beatmapInfo, currentRuleset.Value, currentMods.Value, cancellationToken);
@@ -99,42 +99,45 @@ namespace osu.Game.Beatmaps
}
/// <summary>
/// Retrieves a bindable containing the star difficulty of a <see cref="BeatmapInfo"/> with a given <see cref="RulesetInfo"/> and <see cref="Mod"/> combination.
/// Retrieves a bindable containing the star difficulty of a <see cref="IBeatmapInfo"/> with a given <see cref="RulesetInfo"/> and <see cref="Mod"/> combination.
/// </summary>
/// <remarks>
/// The bindable will not update to follow the currently-selected ruleset and mods or its settings.
/// </remarks>
/// <param name="beatmapInfo">The <see cref="BeatmapInfo"/> to get the difficulty of.</param>
/// <param name="rulesetInfo">The <see cref="RulesetInfo"/> to get the difficulty with. If <c>null</c>, the <paramref name="beatmapInfo"/>'s ruleset is used.</param>
/// <param name="beatmapInfo">The <see cref="IBeatmapInfo"/> to get the difficulty of.</param>
/// <param name="rulesetInfo">The <see cref="IRulesetInfo"/> to get the difficulty with. If <c>null</c>, the <paramref name="beatmapInfo"/>'s ruleset is used.</param>
/// <param name="mods">The <see cref="Mod"/>s to get the difficulty with. If <c>null</c>, no mods will be assumed.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="BeatmapInfo"/>.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="IBeatmapInfo"/>.</param>
/// <returns>A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state.</returns>
public IBindable<StarDifficulty?> GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable<Mod> mods,
public IBindable<StarDifficulty?> GetBindableDifficulty([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo rulesetInfo, [CanBeNull] IEnumerable<Mod> mods,
CancellationToken cancellationToken = default)
=> createBindable(beatmapInfo, rulesetInfo, mods, cancellationToken);
/// <summary>
/// Retrieves the difficulty of a <see cref="BeatmapInfo"/>.
/// Retrieves the difficulty of a <see cref="IBeatmapInfo"/>.
/// </summary>
/// <param name="beatmapInfo">The <see cref="BeatmapInfo"/> to get the difficulty of.</param>
/// <param name="rulesetInfo">The <see cref="RulesetInfo"/> to get the difficulty with.</param>
/// <param name="beatmapInfo">The <see cref="IBeatmapInfo"/> to get the difficulty of.</param>
/// <param name="rulesetInfo">The <see cref="IRulesetInfo"/> to get the difficulty with.</param>
/// <param name="mods">The <see cref="Mod"/>s to get the difficulty with.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops computing the star difficulty.</param>
/// <returns>The <see cref="StarDifficulty"/>.</returns>
public virtual Task<StarDifficulty> GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null,
public virtual Task<StarDifficulty> GetDifficultyAsync([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo rulesetInfo = null,
[CanBeNull] IEnumerable<Mod> mods = null, CancellationToken cancellationToken = default)
{
// In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset.
rulesetInfo ??= beatmapInfo.Ruleset;
var localBeatmapInfo = beatmapInfo as BeatmapInfo;
var localRulesetInfo = rulesetInfo as RulesetInfo;
// Difficulty can only be computed if the beatmap and ruleset are locally available.
if (beatmapInfo.ID == 0 || rulesetInfo.ID == null)
if (localBeatmapInfo == null || localBeatmapInfo.ID == 0 || localRulesetInfo == null)
{
// If not, fall back to the existing star difficulty (e.g. from an online source).
return Task.FromResult(new StarDifficulty(beatmapInfo.StarDifficulty, beatmapInfo.MaxCombo ?? 0));
return Task.FromResult(new StarDifficulty(beatmapInfo.StarRating, (beatmapInfo as IBeatmapOnlineInfo)?.MaxCombo ?? 0));
}
return GetAsync(new DifficultyCacheLookup(beatmapInfo, rulesetInfo, mods), cancellationToken);
return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken);
}
protected override Task<StarDifficulty> ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
@@ -227,12 +230,12 @@ namespace osu.Game.Beatmaps
/// <summary>
/// Creates a new <see cref="BindableStarDifficulty"/> and triggers an initial value update.
/// </summary>
/// <param name="beatmapInfo">The <see cref="BeatmapInfo"/> that star difficulty should correspond to.</param>
/// <param name="initialRulesetInfo">The initial <see cref="RulesetInfo"/> to get the difficulty with.</param>
/// <param name="beatmapInfo">The <see cref="IBeatmapInfo"/> that star difficulty should correspond to.</param>
/// <param name="initialRulesetInfo">The initial <see cref="IRulesetInfo"/> to get the difficulty with.</param>
/// <param name="initialMods">The initial <see cref="Mod"/>s to get the difficulty with.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="BeatmapInfo"/>.</param>
/// <param name="cancellationToken">An optional <see cref="CancellationToken"/> which stops updating the star difficulty for the given <see cref="IBeatmapInfo"/>.</param>
/// <returns>The <see cref="BindableStarDifficulty"/>.</returns>
private BindableStarDifficulty createBindable([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable<Mod> initialMods,
private BindableStarDifficulty createBindable([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable<Mod> initialMods,
CancellationToken cancellationToken)
{
var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken);
@@ -244,12 +247,12 @@ namespace osu.Game.Beatmaps
/// Updates the value of a <see cref="BindableStarDifficulty"/> with a given ruleset + mods.
/// </summary>
/// <param name="bindable">The <see cref="BindableStarDifficulty"/> to update.</param>
/// <param name="rulesetInfo">The <see cref="RulesetInfo"/> to update with.</param>
/// <param name="rulesetInfo">The <see cref="IRulesetInfo"/> to update with.</param>
/// <param name="mods">The <see cref="Mod"/>s to update with.</param>
/// <param name="cancellationToken">A token that may be used to cancel this update.</param>
private void updateBindable([NotNull] BindableStarDifficulty bindable, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
private void updateBindable([NotNull] BindableStarDifficulty bindable, [CanBeNull] IRulesetInfo rulesetInfo, [CanBeNull] IEnumerable<Mod> mods, CancellationToken cancellationToken = default)
{
// GetDifficultyAsync will fall back to existing data from BeatmapInfo if not locally available
// GetDifficultyAsync will fall back to existing data from IBeatmapInfo if not locally available
// (contrary to GetAsync)
GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken)
.ContinueWith(t =>
@@ -343,10 +346,10 @@ namespace osu.Game.Beatmaps
private class BindableStarDifficulty : Bindable<StarDifficulty?>
{
public readonly BeatmapInfo BeatmapInfo;
public readonly IBeatmapInfo BeatmapInfo;
public readonly CancellationToken CancellationToken;
public BindableStarDifficulty(BeatmapInfo beatmapInfo, CancellationToken cancellationToken)
public BindableStarDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken)
{
BeatmapInfo = beatmapInfo;
CancellationToken = cancellationToken;
+2 -2
View File
@@ -16,9 +16,9 @@ namespace osu.Game.Beatmaps
/// <summary>
/// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
/// </summary>
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo, bool includeDifficultyName = true)
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo, bool includeDifficultyName = true, bool includeCreator = true)
{
var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable();
var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(includeCreator);
if (includeDifficultyName)
{
+17
View File
@@ -249,6 +249,23 @@ namespace osu.Game.Beatmaps
public IBindable<WeakReference<ArchiveDownloadRequest<BeatmapSetInfo>>> DownloadFailed => beatmapModelDownloader.DownloadFailed;
// Temporary method until this class supports IBeatmapSetInfo or otherwise.
public bool Download(IBeatmapSetInfo model, bool minimiseDownloadSize = false)
{
return beatmapModelDownloader.Download(new BeatmapSetInfo
{
OnlineBeatmapSetID = model.OnlineID,
Metadata = new BeatmapMetadata
{
Title = model.Metadata?.Title,
Artist = model.Metadata?.Artist,
TitleUnicode = model.Metadata?.TitleUnicode,
ArtistUnicode = model.Metadata?.ArtistUnicode,
Author = new User { Username = model.Metadata?.Author },
}
}, minimiseDownloadSize);
}
public bool Download(BeatmapSetInfo model, bool minimiseDownloadSize = false)
{
return beatmapModelDownloader.Download(model, minimiseDownloadSize);
@@ -34,9 +34,9 @@ namespace osu.Game.Beatmaps
/// <summary>
/// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
/// </summary>
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo)
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo, bool includeCreator = true)
{
string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
string author = !includeCreator || string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode;
string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode;
@@ -13,6 +13,9 @@ namespace osu.Game.Beatmaps
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
new DownloadBeatmapSetRequest(set, minimiseDownloadSize);
public override ArchiveDownloadRequest<BeatmapSetInfo> GetExistingDownload(BeatmapSetInfo model)
=> CurrentDownloads.Find(r => r.Model.OnlineID == model.OnlineID);
public BeatmapModelDownloader(IBeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null)
: base(beatmapModelManager, api, host)
{
+2 -1
View File
@@ -14,7 +14,7 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Beatmaps
{
[ExcludeFromDynamicCompile]
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles<BeatmapSetFileInfo>, ISoftDelete, IEquatable<BeatmapSetInfo>, IBeatmapSetInfo, IBeatmapSetOnlineInfo
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles<BeatmapSetFileInfo>, ISoftDelete, IEquatable<BeatmapSetInfo>, IBeatmapSetInfo
{
public int ID { get; set; }
@@ -35,6 +35,7 @@ namespace osu.Game.Beatmaps
[NotNull]
public List<BeatmapSetFileInfo> Files { get; set; } = new List<BeatmapSetFileInfo>();
// This field is temporary and only used by `APIBeatmapSet.ToBeatmapSet` (soon to be removed) and tests (to be updated to provide APIBeatmapSet instead).
[NotMapped]
public APIBeatmapSet OnlineInfo { get; set; }

Some files were not shown because too many files have changed in this diff Show More