mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 19:03:08 +08:00
Merge branch 'master' into android
This commit is contained in:
commit
17d103181c
@ -32,7 +32,7 @@ If you are not interested in developing the game, you can consume our [binary re
|
|||||||
| ------------- | ------------- |
|
| ------------- | ------------- |
|
||||||
|
|
||||||
- **Linux** users are recommended to self-compile until we have official deployment in place.
|
- **Linux** users are recommended to self-compile until we have official deployment in place.
|
||||||
- **iOS** users can join the [TestFlight beta program](https://t.co/xQJmHkfC18) (note that due to high demand this is regularly full).
|
- **iOS** users can join the [TestFlight beta program](https://t.co/PasE1zrHhw) (note that due to high demand this is regularly full).
|
||||||
- **Android** users can self-compile, and expect a public beta soon.
|
- **Android** users can self-compile, and expect a public beta soon.
|
||||||
|
|
||||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||||
|
@ -7,7 +7,6 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Desktop.Overlays;
|
using osu.Desktop.Overlays;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
@ -56,7 +55,7 @@ namespace osu.Desktop
|
|||||||
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v =>
|
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v =>
|
||||||
{
|
{
|
||||||
Add(v);
|
Add(v);
|
||||||
v.State = Visibility.Visible;
|
v.Show();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
||||||
@ -74,13 +73,11 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
case Intro _:
|
case Intro _:
|
||||||
case MainMenu _:
|
case MainMenu _:
|
||||||
if (versionManager != null)
|
versionManager?.Show();
|
||||||
versionManager.State = Visibility.Visible;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (versionManager != null)
|
versionManager?.Hide();
|
||||||
versionManager.State = Visibility.Hidden;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ namespace osu.Desktop.Overlays
|
|||||||
|
|
||||||
Activated = delegate
|
Activated = delegate
|
||||||
{
|
{
|
||||||
changelog.ShowBuild("lazer", version);
|
changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||||
{
|
{
|
||||||
if (beatmap.HitObjects.Count == 0)
|
if (beatmap.HitObjects.Count == 0)
|
||||||
return new CatchDifficultyAttributes { Mods = mods };
|
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
|
||||||
|
|
||||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||||
@ -41,7 +41,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
|
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
|
||||||
Mods = mods,
|
Mods = mods,
|
||||||
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
||||||
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet))
|
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)),
|
||||||
|
Skills = skills
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +43,13 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
float positionChange = Math.Abs(lastPosition - h.X);
|
float positionChange = Math.Abs(lastPosition - h.X);
|
||||||
double timeAvailable = h.StartTime - lastTime;
|
double timeAvailable = h.StartTime - lastTime;
|
||||||
|
|
||||||
//So we can either make it there without a dash or not.
|
// So we can either make it there without a dash or not.
|
||||||
double speedRequired = positionChange / timeAvailable;
|
// If positionChange is 0, we don't need to move, so speedRequired should also be 0 (could be NaN if timeAvailable is 0 too)
|
||||||
|
// The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour.
|
||||||
|
double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable;
|
||||||
|
|
||||||
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
|
bool dashRequired = speedRequired > movement_speed;
|
||||||
|
bool impossibleJump = speedRequired > movement_speed * 2;
|
||||||
|
|
||||||
// todo: get correct catcher size, based on difficulty CS.
|
// todo: get correct catcher size, based on difficulty CS.
|
||||||
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
|
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
|
||||||
@ -59,9 +62,8 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h is Banana)
|
if (impossibleJump)
|
||||||
{
|
{
|
||||||
// auto bananas unrealistically warp to catch 100% combo.
|
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||||
}
|
}
|
||||||
else if (h.HyperDash)
|
else if (h.HyperDash)
|
||||||
|
@ -379,8 +379,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
|
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if (hyperDashDirection > 0 && hyperDashTargetPosition < X ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
hyperDashDirection < 0 && hyperDashTargetPosition > X)
|
(hyperDashDirection < 0 && hyperDashTargetPosition > X))
|
||||||
{
|
{
|
||||||
X = hyperDashTargetPosition;
|
X = hyperDashTargetPosition;
|
||||||
SetHyperDashState();
|
SetHyperDashState();
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||||
{
|
{
|
||||||
if (beatmap.HitObjects.Count == 0)
|
if (beatmap.HitObjects.Count == 0)
|
||||||
return new ManiaDifficultyAttributes { Mods = mods };
|
return new ManiaDifficultyAttributes { Mods = mods, Skills = skills };
|
||||||
|
|
||||||
return new ManiaDifficultyAttributes
|
return new ManiaDifficultyAttributes
|
||||||
{
|
{
|
||||||
@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
Mods = mods,
|
Mods = mods,
|
||||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
|
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
|
||||||
|
Skills = skills
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,11 +46,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
AddStep("move mouse away", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.TopLeft));
|
AddStep("move mouse away", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.TopLeft));
|
||||||
AddStep("click", () => osuInputManager.GameClick());
|
AddStep("click", () => osuInputManager.GameClick());
|
||||||
AddAssert("not dismissed", () => !resumeFired && resume.State == Visibility.Visible);
|
AddAssert("not dismissed", () => !resumeFired && resume.State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
|
AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
|
||||||
AddStep("click", () => osuInputManager.GameClick());
|
AddStep("click", () => osuInputManager.GameClick());
|
||||||
AddAssert("dismissed", () => resumeFired && resume.State == Visibility.Hidden);
|
AddAssert("dismissed", () => resumeFired && resume.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ManualOsuInputManager : OsuInputManager
|
private class ManualOsuInputManager : OsuInputManager
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance
|
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance
|
||||||
|| stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
|
|| (stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance))
|
||||||
{
|
{
|
||||||
stackBaseIndex = n;
|
stackBaseIndex = n;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||||
{
|
{
|
||||||
if (beatmap.HitObjects.Count == 0)
|
if (beatmap.HitObjects.Count == 0)
|
||||||
return new OsuDifficultyAttributes { Mods = mods };
|
return new OsuDifficultyAttributes { Mods = mods, Skills = skills };
|
||||||
|
|
||||||
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
@ -50,7 +50,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
SpeedStrain = speedRating,
|
SpeedStrain = speedRating,
|
||||||
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
||||||
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
||||||
MaxCombo = maxCombo
|
MaxCombo = maxCombo,
|
||||||
|
Skills = skills
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -11,7 +14,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
internal class OsuModGrow : Mod, IApplicableToDrawableHitObjects
|
internal class OsuModGrow : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
|
||||||
{
|
{
|
||||||
public override string Name => "Grow";
|
public override string Name => "Grow";
|
||||||
|
|
||||||
@ -25,9 +28,16 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
|
||||||
|
|
||||||
|
public void ReadFromConfig(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
increaseFirstObjectVisibility = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||||
{
|
{
|
||||||
foreach (var drawable in drawables)
|
foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
|
||||||
{
|
{
|
||||||
switch (drawable)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
|
@ -37,11 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
|
if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
|
||||||
|
|
||||||
if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit)
|
if ((osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime) || osuHit.IsHit)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
|
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
|
||||||
requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner;
|
requiresHold |= (osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered)) || osuHit is DrawableSpinner;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requiresHit)
|
if (requiresHit)
|
||||||
|
@ -6,7 +6,7 @@ using System.Diagnostics;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
using osu.Framework.Graphics.Batches;
|
||||||
using osu.Framework.Graphics.OpenGL.Vertices;
|
using osu.Framework.Graphics.OpenGL.Vertices;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.Shaders;
|
using osu.Framework.Graphics.Shaders;
|
||||||
@ -57,7 +57,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
// InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node
|
// InvalidationID 1 forces an update of each part of the cursor trail the first time ApplyState is run on the draw node
|
||||||
// This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms
|
// This is to prevent garbage data from being sent to the vertex shader, resulting in visual issues on some platforms
|
||||||
parts[i].InvalidationID = 1;
|
parts[i].InvalidationID = 1;
|
||||||
parts[i].WasUpdated = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +148,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
public Vector2 Position;
|
public Vector2 Position;
|
||||||
public float Time;
|
public float Time;
|
||||||
public long InvalidationID;
|
public long InvalidationID;
|
||||||
public bool WasUpdated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TrailDrawNode : DrawNode
|
private class TrailDrawNode : DrawNode
|
||||||
@ -164,16 +162,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
private Vector2 size;
|
private Vector2 size;
|
||||||
|
|
||||||
private readonly VertexBuffer<TexturedTrailVertex> vertexBuffer = new QuadVertexBuffer<TexturedTrailVertex>(max_sprites, BufferUsageHint.DynamicDraw);
|
private readonly VertexBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
|
||||||
|
|
||||||
public TrailDrawNode(CursorTrail source)
|
public TrailDrawNode(CursorTrail source)
|
||||||
: base(source)
|
: base(source)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < max_sprites; i++)
|
for (int i = 0; i < max_sprites; i++)
|
||||||
{
|
|
||||||
parts[i].InvalidationID = 0;
|
parts[i].InvalidationID = 0;
|
||||||
parts[i].WasUpdated = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ApplyState()
|
public override void ApplyState()
|
||||||
@ -194,56 +189,29 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
||||||
{
|
{
|
||||||
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
|
||||||
|
|
||||||
int updateStart = -1, updateEnd = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < parts.Length; ++i)
|
|
||||||
{
|
|
||||||
if (parts[i].WasUpdated)
|
|
||||||
{
|
|
||||||
if (updateStart == -1)
|
|
||||||
updateStart = i;
|
|
||||||
updateEnd = i + 1;
|
|
||||||
|
|
||||||
int start = i * 4;
|
|
||||||
int end = start;
|
|
||||||
|
|
||||||
Vector2 pos = parts[i].Position;
|
|
||||||
float localTime = parts[i].Time;
|
|
||||||
|
|
||||||
DrawQuad(
|
|
||||||
texture,
|
|
||||||
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
|
|
||||||
DrawColourInfo.Colour,
|
|
||||||
null,
|
|
||||||
v => vertexBuffer.Vertices[end++] = new TexturedTrailVertex
|
|
||||||
{
|
|
||||||
Position = v.Position,
|
|
||||||
TexturePosition = v.TexturePosition,
|
|
||||||
Time = localTime + 1,
|
|
||||||
Colour = v.Colour,
|
|
||||||
});
|
|
||||||
|
|
||||||
parts[i].WasUpdated = false;
|
|
||||||
}
|
|
||||||
else if (updateStart != -1)
|
|
||||||
{
|
|
||||||
vertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4);
|
|
||||||
updateStart = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update all remaining vertices that have been changed.
|
|
||||||
if (updateStart != -1)
|
|
||||||
vertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4);
|
|
||||||
|
|
||||||
base.Draw(vertexAction);
|
base.Draw(vertexAction);
|
||||||
|
|
||||||
shader.Bind();
|
shader.Bind();
|
||||||
|
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
||||||
|
|
||||||
texture.TextureGL.Bind();
|
for (int i = 0; i < parts.Length; ++i)
|
||||||
vertexBuffer.Draw();
|
{
|
||||||
|
Vector2 pos = parts[i].Position;
|
||||||
|
float localTime = parts[i].Time;
|
||||||
|
|
||||||
|
DrawQuad(
|
||||||
|
texture,
|
||||||
|
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
|
||||||
|
DrawColourInfo.Colour,
|
||||||
|
null,
|
||||||
|
v => vertexBatch.Add(new TexturedTrailVertex
|
||||||
|
{
|
||||||
|
Position = v.Position,
|
||||||
|
TexturePosition = v.TexturePosition,
|
||||||
|
Time = localTime + 1,
|
||||||
|
Colour = v.Colour,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
shader.Unbind();
|
shader.Unbind();
|
||||||
}
|
}
|
||||||
@ -252,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
vertexBuffer.Dispose();
|
vertexBatch.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
private GameplayCursorContainer localCursorContainer;
|
private GameplayCursorContainer localCursorContainer;
|
||||||
|
|
||||||
public override CursorContainer LocalCursor => State == Visibility.Visible ? localCursorContainer : null;
|
public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null;
|
||||||
|
|
||||||
protected override string Message => "Click the orange cursor to resume";
|
protected override string Message => "Click the orange cursor to resume";
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||||
{
|
{
|
||||||
if (beatmap.HitObjects.Count == 0)
|
if (beatmap.HitObjects.Count == 0)
|
||||||
return new TaikoDifficultyAttributes { Mods = mods };
|
return new TaikoDifficultyAttributes { Mods = mods, Skills = skills };
|
||||||
|
|
||||||
return new TaikoDifficultyAttributes
|
return new TaikoDifficultyAttributes
|
||||||
{
|
{
|
||||||
@ -36,6 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
|
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
|
||||||
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
||||||
|
Skills = skills
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.IO;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using SharpCompress.Archives.Zip;
|
using SharpCompress.Archives.Zip;
|
||||||
|
|
||||||
@ -21,14 +23,14 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
public class ImportBeatmapTest
|
public class ImportBeatmapTest
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportWhenClosed()
|
public async Task TestImportWhenClosed()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LoadOszIntoOsu(loadOsu(host));
|
await LoadOszIntoOsu(loadOsu(host));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -38,7 +40,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportThenDelete()
|
public async Task TestImportThenDelete()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete"))
|
||||||
@ -47,7 +49,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
deleteBeatmapSet(imported, osu);
|
deleteBeatmapSet(imported, osu);
|
||||||
}
|
}
|
||||||
@ -59,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportThenImport()
|
public async Task TestImportThenImport()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
|
||||||
@ -68,17 +70,15 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
var importedSecondTime = LoadOszIntoOsu(osu);
|
var importedSecondTime = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
|
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
|
||||||
Assert.IsTrue(imported.ID == importedSecondTime.ID);
|
Assert.IsTrue(imported.ID == importedSecondTime.ID);
|
||||||
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
|
Assert.IsTrue(imported.Beatmaps.First().ID == importedSecondTime.Beatmaps.First().ID);
|
||||||
|
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
checkBeatmapSetCount(osu, 1);
|
||||||
|
checkSingleReferencedFileCount(osu, 18);
|
||||||
Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
|
|
||||||
Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -88,30 +88,41 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRollbackOnFailure()
|
public async Task TestRollbackOnFailure()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
int itemAddRemoveFireCount = 0;
|
||||||
|
int loggedExceptionCount = 0;
|
||||||
|
|
||||||
|
Logger.NewEntry += l =>
|
||||||
|
{
|
||||||
|
if (l.Target == LoggingTarget.Database && l.Exception != null)
|
||||||
|
Interlocked.Increment(ref loggedExceptionCount);
|
||||||
|
};
|
||||||
|
|
||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
int fireCount = 0;
|
|
||||||
|
|
||||||
// ReSharper disable once AccessToModifiedClosure
|
// ReSharper disable once AccessToModifiedClosure
|
||||||
manager.ItemAdded += (_, __) => fireCount++;
|
manager.ItemAdded += (_, __) => Interlocked.Increment(ref itemAddRemoveFireCount);
|
||||||
manager.ItemRemoved += _ => fireCount++;
|
manager.ItemRemoved += _ => Interlocked.Increment(ref itemAddRemoveFireCount);
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
Assert.AreEqual(0, fireCount -= 1);
|
Assert.AreEqual(0, itemAddRemoveFireCount -= 1);
|
||||||
|
|
||||||
imported.Hash += "-changed";
|
imported.Hash += "-changed";
|
||||||
manager.Update(imported);
|
manager.Update(imported);
|
||||||
|
|
||||||
Assert.AreEqual(0, fireCount -= 2);
|
Assert.AreEqual(0, itemAddRemoveFireCount -= 2);
|
||||||
|
|
||||||
|
checkBeatmapSetCount(osu, 1);
|
||||||
|
checkBeatmapCount(osu, 12);
|
||||||
|
checkSingleReferencedFileCount(osu, 18);
|
||||||
|
|
||||||
var breakTemp = TestResources.GetTestBeatmapForImport();
|
var breakTemp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
@ -127,19 +138,24 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
zip.SaveTo(outStream, SharpCompress.Common.CompressionType.Deflate);
|
zip.SaveTo(outStream, SharpCompress.Common.CompressionType.Deflate);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
|
|
||||||
Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
|
|
||||||
Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count);
|
|
||||||
|
|
||||||
// this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
|
// this will trigger purging of the existing beatmap (online set id match) but should rollback due to broken osu.
|
||||||
manager.Import(breakTemp);
|
try
|
||||||
|
{
|
||||||
|
await manager.Import(breakTemp);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// no events should be fired in the case of a rollback.
|
// no events should be fired in the case of a rollback.
|
||||||
Assert.AreEqual(0, fireCount);
|
Assert.AreEqual(0, itemAddRemoveFireCount);
|
||||||
|
|
||||||
Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
|
checkBeatmapSetCount(osu, 1);
|
||||||
Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
|
checkBeatmapCount(osu, 12);
|
||||||
Assert.AreEqual(12, manager.QueryBeatmaps(_ => true).ToList().Count);
|
|
||||||
|
checkSingleReferencedFileCount(osu, 18);
|
||||||
|
|
||||||
|
Assert.AreEqual(1, loggedExceptionCount);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -149,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportThenImportDifferentHash()
|
public async Task TestImportThenImportDifferentHash()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash"))
|
||||||
@ -159,19 +175,18 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
imported.Hash += "-changed";
|
imported.Hash += "-changed";
|
||||||
manager.Update(imported);
|
manager.Update(imported);
|
||||||
|
|
||||||
var importedSecondTime = LoadOszIntoOsu(osu);
|
var importedSecondTime = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
Assert.IsTrue(imported.ID != importedSecondTime.ID);
|
Assert.IsTrue(imported.ID != importedSecondTime.ID);
|
||||||
Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID);
|
Assert.IsTrue(imported.Beatmaps.First().ID < importedSecondTime.Beatmaps.First().ID);
|
||||||
|
|
||||||
// only one beatmap will exist as the online set ID matched, causing purging of the first import.
|
// only one beatmap will exist as the online set ID matched, causing purging of the first import.
|
||||||
Assert.AreEqual(1, manager.GetAllUsableBeatmapSets().Count);
|
checkBeatmapSetCount(osu, 1);
|
||||||
Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -181,7 +196,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportThenDeleteThenImport()
|
public async Task TestImportThenDeleteThenImport()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport"))
|
||||||
@ -190,11 +205,11 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
deleteBeatmapSet(imported, osu);
|
deleteBeatmapSet(imported, osu);
|
||||||
|
|
||||||
var importedSecondTime = LoadOszIntoOsu(osu);
|
var importedSecondTime = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
|
// check the newly "imported" beatmap is actually just the restored previous import. since it matches hash.
|
||||||
Assert.IsTrue(imported.ID == importedSecondTime.ID);
|
Assert.IsTrue(imported.ID == importedSecondTime.ID);
|
||||||
@ -209,7 +224,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
|
public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"TestImportThenDeleteThenImport-{set}"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"TestImportThenDeleteThenImport-{set}"))
|
||||||
@ -218,7 +233,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
|
|
||||||
var imported = LoadOszIntoOsu(osu);
|
var imported = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
if (set)
|
if (set)
|
||||||
imported.OnlineBeatmapSetID = 1234;
|
imported.OnlineBeatmapSetID = 1234;
|
||||||
@ -229,7 +244,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
deleteBeatmapSet(imported, osu);
|
deleteBeatmapSet(imported, osu);
|
||||||
|
|
||||||
var importedSecondTime = LoadOszIntoOsu(osu);
|
var importedSecondTime = await LoadOszIntoOsu(osu);
|
||||||
|
|
||||||
// check the newly "imported" beatmap has been reimported due to mismatch (even though hashes matched)
|
// check the newly "imported" beatmap has been reimported due to mismatch (even though hashes matched)
|
||||||
Assert.IsTrue(imported.ID != importedSecondTime.ID);
|
Assert.IsTrue(imported.ID != importedSecondTime.ID);
|
||||||
@ -243,7 +258,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportWithDuplicateBeatmapIDs()
|
public async Task TestImportWithDuplicateBeatmapIDs()
|
||||||
{
|
{
|
||||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDuplicateBeatmapID"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDuplicateBeatmapID"))
|
||||||
@ -284,7 +299,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
|
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
var imported = manager.Import(toImport);
|
var imported = await manager.Import(toImport);
|
||||||
|
|
||||||
Assert.NotNull(imported);
|
Assert.NotNull(imported);
|
||||||
Assert.AreEqual(null, imported.Beatmaps[0].OnlineBeatmapID);
|
Assert.AreEqual(null, imported.Beatmaps[0].OnlineBeatmapID);
|
||||||
@ -330,7 +345,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportWhenFileOpen()
|
public async Task TestImportWhenFileOpen()
|
||||||
{
|
{
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen"))
|
||||||
{
|
{
|
||||||
@ -339,7 +354,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
var osu = loadOsu(host);
|
var osu = loadOsu(host);
|
||||||
var temp = TestResources.GetTestBeatmapForImport();
|
var temp = TestResources.GetTestBeatmapForImport();
|
||||||
using (File.OpenRead(temp))
|
using (File.OpenRead(temp))
|
||||||
osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||||
ensureLoaded(osu);
|
ensureLoaded(osu);
|
||||||
File.Delete(temp);
|
File.Delete(temp);
|
||||||
Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
|
Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
|
||||||
@ -351,13 +366,13 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BeatmapSetInfo LoadOszIntoOsu(OsuGameBase osu, string path = null)
|
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null)
|
||||||
{
|
{
|
||||||
var temp = path ?? TestResources.GetTestBeatmapForImport();
|
var temp = path ?? TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
manager.Import(temp);
|
await manager.Import(temp);
|
||||||
|
|
||||||
var imported = manager.GetAllUsableBeatmapSets();
|
var imported = manager.GetAllUsableBeatmapSets();
|
||||||
|
|
||||||
@ -373,11 +388,32 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
manager.Delete(imported);
|
manager.Delete(imported);
|
||||||
|
|
||||||
Assert.IsTrue(manager.GetAllUsableBeatmapSets().Count == 0);
|
checkBeatmapSetCount(osu, 0);
|
||||||
Assert.AreEqual(1, manager.QueryBeatmapSets(_ => true).ToList().Count);
|
checkBeatmapSetCount(osu, 1, true);
|
||||||
|
checkSingleReferencedFileCount(osu, 0);
|
||||||
|
|
||||||
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
|
Assert.IsTrue(manager.QueryBeatmapSets(_ => true).First().DeletePending);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkBeatmapSetCount(OsuGameBase osu, int expected, bool includeDeletePending = false)
|
||||||
|
{
|
||||||
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
|
Assert.AreEqual(expected, includeDeletePending
|
||||||
|
? manager.QueryBeatmapSets(_ => true).ToList().Count
|
||||||
|
: manager.GetAllUsableBeatmapSets().Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBeatmapCount(OsuGameBase osu, int expected)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expected, osu.Dependencies.Get<BeatmapManager>().QueryBeatmaps(_ => true).ToList().Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkSingleReferencedFileCount(OsuGameBase osu, int expected)
|
||||||
|
{
|
||||||
|
Assert.AreEqual(expected, osu.Dependencies.Get<FileStore>().QueryFiles(f => f.ReferenceCount == 1).Count());
|
||||||
|
}
|
||||||
|
|
||||||
private OsuGameBase loadOsu(GameHost host)
|
private OsuGameBase loadOsu(GameHost host)
|
||||||
{
|
{
|
||||||
var osu = new OsuGameBase();
|
var osu = new OsuGameBase();
|
||||||
|
@ -23,13 +23,13 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
public class ImportScoreTest
|
public class ImportScoreTest
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBasicImport()
|
public async Task TestBasicImport()
|
||||||
{
|
{
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestBasicImport"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestBasicImport"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = await loadOsu(host);
|
||||||
|
|
||||||
var toImport = new ScoreInfo
|
var toImport = new ScoreInfo
|
||||||
{
|
{
|
||||||
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
OnlineScoreID = 12345,
|
OnlineScoreID = 12345,
|
||||||
};
|
};
|
||||||
|
|
||||||
var imported = loadIntoOsu(osu, toImport);
|
var imported = await loadIntoOsu(osu, toImport);
|
||||||
|
|
||||||
Assert.AreEqual(toImport.Rank, imported.Rank);
|
Assert.AreEqual(toImport.Rank, imported.Rank);
|
||||||
Assert.AreEqual(toImport.TotalScore, imported.TotalScore);
|
Assert.AreEqual(toImport.TotalScore, imported.TotalScore);
|
||||||
@ -62,20 +62,20 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportMods()
|
public async Task TestImportMods()
|
||||||
{
|
{
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportMods"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportMods"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = await loadOsu(host);
|
||||||
|
|
||||||
var toImport = new ScoreInfo
|
var toImport = new ScoreInfo
|
||||||
{
|
{
|
||||||
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
|
||||||
};
|
};
|
||||||
|
|
||||||
var imported = loadIntoOsu(osu, toImport);
|
var imported = await loadIntoOsu(osu, toImport);
|
||||||
|
|
||||||
Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock));
|
Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock));
|
||||||
Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime));
|
Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime));
|
||||||
@ -88,13 +88,13 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestImportStatistics()
|
public async Task TestImportStatistics()
|
||||||
{
|
{
|
||||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportStatistics"))
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportStatistics"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var osu = loadOsu(host);
|
var osu = await loadOsu(host);
|
||||||
|
|
||||||
var toImport = new ScoreInfo
|
var toImport = new ScoreInfo
|
||||||
{
|
{
|
||||||
@ -105,7 +105,7 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var imported = loadIntoOsu(osu, toImport);
|
var imported = await loadIntoOsu(osu, toImport);
|
||||||
|
|
||||||
Assert.AreEqual(toImport.Statistics[HitResult.Perfect], imported.Statistics[HitResult.Perfect]);
|
Assert.AreEqual(toImport.Statistics[HitResult.Perfect], imported.Statistics[HitResult.Perfect]);
|
||||||
Assert.AreEqual(toImport.Statistics[HitResult.Miss], imported.Statistics[HitResult.Miss]);
|
Assert.AreEqual(toImport.Statistics[HitResult.Miss], imported.Statistics[HitResult.Miss]);
|
||||||
@ -117,7 +117,7 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScoreInfo loadIntoOsu(OsuGameBase osu, ScoreInfo score)
|
private async Task<ScoreInfo> loadIntoOsu(OsuGameBase osu, ScoreInfo score)
|
||||||
{
|
{
|
||||||
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
@ -125,20 +125,24 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
score.Ruleset = new OsuRuleset().RulesetInfo;
|
score.Ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
||||||
scoreManager.Import(score);
|
await scoreManager.Import(score);
|
||||||
|
|
||||||
return scoreManager.GetAllUsableScores().First();
|
return scoreManager.GetAllUsableScores().First();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OsuGameBase loadOsu(GameHost host)
|
private async Task<OsuGameBase> loadOsu(GameHost host)
|
||||||
{
|
{
|
||||||
var osu = new OsuGameBase();
|
var osu = new OsuGameBase();
|
||||||
|
|
||||||
|
#pragma warning disable 4014
|
||||||
Task.Run(() => host.Run(osu));
|
Task.Run(() => host.Run(osu));
|
||||||
|
#pragma warning restore 4014
|
||||||
|
|
||||||
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||||
|
|
||||||
var beatmapFile = TestResources.GetTestBeatmapForImport();
|
var beatmapFile = TestResources.GetTestBeatmapForImport();
|
||||||
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
beatmapManager.Import(beatmapFile);
|
await beatmapManager.Import(beatmapFile);
|
||||||
|
|
||||||
return osu;
|
return osu;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default));
|
||||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||||
|
|
||||||
manager.Import(TestResources.GetTestBeatmapForImport());
|
manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||||
|
|
||||||
Beatmap.SetDefault();
|
Beatmap.SetDefault();
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
protected override void AddCheckSteps()
|
protected override void AddCheckSteps()
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State == Visibility.Visible);
|
AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State.Value == Visibility.Visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FailPlayer : TestPlayer
|
private class FailPlayer : TestPlayer
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||||
|
|
||||||
AddStep("Press select", () => press(GlobalAction.Select));
|
AddStep("Press select", () => press(GlobalAction.Select));
|
||||||
AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
|
AddAssert("Overlay still open", () => pauseOverlay.State.Value == Visibility.Visible);
|
||||||
|
|
||||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||||
}
|
}
|
||||||
@ -237,7 +237,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Action was triggered", () => triggered);
|
AddAssert("Action was triggered", () => triggered);
|
||||||
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
|
AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -272,7 +272,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
return triggered;
|
return triggered;
|
||||||
});
|
});
|
||||||
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
|
AddAssert("Overlay is closed", () => pauseOverlay.State.Value == Visibility.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void press(Key key)
|
private void press(Key key)
|
||||||
|
109
osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
Normal file
109
osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// 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.Audio;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneGameplayRewinding : PlayerTestScene
|
||||||
|
{
|
||||||
|
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private AudioManager audioManager { get; set; }
|
||||||
|
|
||||||
|
public TestSceneGameplayRewinding()
|
||||||
|
: base(new OsuRuleset())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private Track track;
|
||||||
|
|
||||||
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||||
|
track = working.Track;
|
||||||
|
return working;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoJudgementsOnRewind()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||||
|
addSeekStep(3000);
|
||||||
|
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
|
||||||
|
AddStep("clear results", () => player.AppliedResults.Clear());
|
||||||
|
addSeekStep(0);
|
||||||
|
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
|
||||||
|
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSeekStep(double time)
|
||||||
|
{
|
||||||
|
AddStep($"seek to {time}", () => track.Seek(time));
|
||||||
|
|
||||||
|
// Allow a few frames of lenience
|
||||||
|
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
|
||||||
|
return new RulesetExposingPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
beatmap.HitObjects.Add(new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(256, 192),
|
||||||
|
StartTime = 1000 + 30 * i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RulesetExposingPlayer : Player
|
||||||
|
{
|
||||||
|
public readonly List<JudgementResult> AppliedResults = new List<JudgementResult>();
|
||||||
|
|
||||||
|
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
||||||
|
|
||||||
|
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
|
||||||
|
|
||||||
|
public RulesetExposingPlayer()
|
||||||
|
: base(false, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -189,7 +189,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddAssert("pause overlay " + (isShown ? "shown" : "hidden"), () => Player.PauseOverlayVisible == isShown);
|
AddAssert("pause overlay " + (isShown ? "shown" : "hidden"), () => Player.PauseOverlayVisible == isShown);
|
||||||
|
|
||||||
private void confirmClockRunning(bool isRunning) =>
|
private void confirmClockRunning(bool isRunning) =>
|
||||||
AddAssert("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning);
|
AddUntilStep("clock " + (isRunning ? "running" : "stopped"), () => Player.GameplayClockContainer.GameplayClock.IsRunning == isRunning);
|
||||||
|
|
||||||
protected override bool AllowFail => true;
|
protected override bool AllowFail => true;
|
||||||
|
|
||||||
@ -203,9 +203,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
public new HUDOverlay HUDOverlay => base.HUDOverlay;
|
||||||
|
|
||||||
public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible;
|
public bool FailOverlayVisible => FailOverlay.State.Value == Visibility.Visible;
|
||||||
|
|
||||||
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
|
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;
|
||||||
|
|
||||||
public override void OnEntering(IScreen last)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
State = Visibility.Visible,
|
State = { Value = Visibility.Visible },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("Restart", restart);
|
AddStep("Restart", restart);
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
|
|
||||||
public TestSceneToolbar()
|
public TestSceneToolbar()
|
||||||
{
|
{
|
||||||
var toolbar = new Toolbar { State = Visibility.Visible };
|
var toolbar = new Toolbar { State = { Value = Visibility.Visible } };
|
||||||
ToolbarNotificationButton notificationButton = null;
|
ToolbarNotificationButton notificationButton = null;
|
||||||
|
|
||||||
AddStep("create toolbar", () =>
|
AddStep("create toolbar", () =>
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
settings = new TestRoomSettings
|
settings = new TestRoomSettings
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
State = Visibility.Visible
|
State = { Value = Visibility.Visible }
|
||||||
};
|
};
|
||||||
|
|
||||||
Child = settings;
|
Child = settings;
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
api.Logout();
|
api.Logout();
|
||||||
api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
|
api.LocalUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
|
||||||
|
|
||||||
AddStep("show", () => accountCreation.State = Visibility.Visible);
|
AddStep("show", () => accountCreation.Show());
|
||||||
AddStep("logout", () => api.Logout());
|
AddStep("logout", () => api.Logout());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
typeof(SuccessRate),
|
typeof(SuccessRate),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private RulesetInfo maniaRuleset;
|
||||||
|
private RulesetInfo taikoRuleset;
|
||||||
|
|
||||||
public TestSceneBeatmapSetOverlay()
|
public TestSceneBeatmapSetOverlay()
|
||||||
{
|
{
|
||||||
Add(overlay = new BeatmapSetOverlay());
|
Add(overlay = new BeatmapSetOverlay());
|
||||||
@ -49,13 +52,25 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets)
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
var mania = rulesets.GetRuleset(3);
|
maniaRuleset = rulesets.GetRuleset(3);
|
||||||
var taiko = rulesets.GetRuleset(1);
|
taikoRuleset = rulesets.GetRuleset(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLoading()
|
||||||
|
{
|
||||||
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
|
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOnline()
|
||||||
|
{
|
||||||
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
|
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLocalBeatmaps()
|
||||||
|
{
|
||||||
AddStep(@"show first", () =>
|
AddStep(@"show first", () =>
|
||||||
{
|
{
|
||||||
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
||||||
@ -87,13 +102,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
|
Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
||||||
Beatmaps = new List<BeatmapInfo>
|
Beatmaps = new List<BeatmapInfo>
|
||||||
{
|
{
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 1.36,
|
StarDifficulty = 1.36,
|
||||||
Version = @"BASIC",
|
Version = @"BASIC",
|
||||||
Ruleset = mania,
|
Ruleset = maniaRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 4,
|
CircleSize = 4,
|
||||||
@ -111,16 +127,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 2.22,
|
StarDifficulty = 2.22,
|
||||||
Version = @"NOVICE",
|
Version = @"NOVICE",
|
||||||
Ruleset = mania,
|
Ruleset = maniaRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 4,
|
CircleSize = 4,
|
||||||
@ -138,16 +153,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 3.49,
|
StarDifficulty = 3.49,
|
||||||
Version = @"ADVANCED",
|
Version = @"ADVANCED",
|
||||||
Ruleset = mania,
|
Ruleset = maniaRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 4,
|
CircleSize = 4,
|
||||||
@ -165,16 +179,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 4.24,
|
StarDifficulty = 4.24,
|
||||||
Version = @"EXHAUST",
|
Version = @"EXHAUST",
|
||||||
Ruleset = mania,
|
Ruleset = maniaRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 4,
|
CircleSize = 4,
|
||||||
@ -192,16 +205,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 5.26,
|
StarDifficulty = 5.26,
|
||||||
Version = @"GRAVITY",
|
Version = @"GRAVITY",
|
||||||
Ruleset = mania,
|
Ruleset = maniaRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 4,
|
CircleSize = 4,
|
||||||
@ -219,9 +231,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -258,13 +269,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
|
Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
||||||
Beatmaps = new List<BeatmapInfo>
|
Beatmaps = new List<BeatmapInfo>
|
||||||
{
|
{
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 1.40,
|
StarDifficulty = 1.40,
|
||||||
Version = @"yzrin's Kantan",
|
Version = @"yzrin's Kantan",
|
||||||
Ruleset = taiko,
|
Ruleset = taikoRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 2,
|
CircleSize = 2,
|
||||||
@ -282,16 +294,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 2.23,
|
StarDifficulty = 2.23,
|
||||||
Version = @"Futsuu",
|
Version = @"Futsuu",
|
||||||
Ruleset = taiko,
|
Ruleset = taikoRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 2,
|
CircleSize = 2,
|
||||||
@ -309,16 +320,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 3.19,
|
StarDifficulty = 3.19,
|
||||||
Version = @"Muzukashii",
|
Version = @"Muzukashii",
|
||||||
Ruleset = taiko,
|
Ruleset = taikoRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 2,
|
CircleSize = 2,
|
||||||
@ -336,16 +346,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 3.97,
|
StarDifficulty = 3.97,
|
||||||
Version = @"Charlotte's Oni",
|
Version = @"Charlotte's Oni",
|
||||||
Ruleset = taiko,
|
Ruleset = taikoRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 5,
|
CircleSize = 5,
|
||||||
@ -363,16 +372,15 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new BeatmapInfo
|
new BeatmapInfo
|
||||||
{
|
{
|
||||||
StarDifficulty = 5.08,
|
StarDifficulty = 5.08,
|
||||||
Version = @"Labyrinth Oni",
|
Version = @"Labyrinth Oni",
|
||||||
Ruleset = taiko,
|
Ruleset = taikoRuleset,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 5,
|
CircleSize = 5,
|
||||||
@ -390,16 +398,24 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHide()
|
||||||
|
{
|
||||||
AddStep(@"hide", overlay.Hide);
|
AddStep(@"hide", overlay.Hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShowWithNoReload()
|
||||||
|
{
|
||||||
AddStep(@"show without reload", overlay.Show);
|
AddStep(@"show without reload", overlay.Show);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
using osu.Game.Screens.Select.Details;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneBeatmapSetOverlayDetails : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(Details)
|
||||||
|
};
|
||||||
|
|
||||||
|
private RatingsExposingDetails details;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = details = new RatingsExposingDetails
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMetrics()
|
||||||
|
{
|
||||||
|
var firstSet = createSet();
|
||||||
|
var secondSet = createSet();
|
||||||
|
|
||||||
|
AddStep("set first set", () => details.BeatmapSet = firstSet);
|
||||||
|
AddAssert("ratings set", () => details.Ratings.Metrics == firstSet.Metrics);
|
||||||
|
|
||||||
|
AddStep("set second set", () => details.BeatmapSet = secondSet);
|
||||||
|
AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics);
|
||||||
|
|
||||||
|
BeatmapSetInfo createSet() => new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() },
|
||||||
|
Beatmaps = new List<BeatmapInfo>
|
||||||
|
{
|
||||||
|
new BeatmapInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapMetrics
|
||||||
|
{
|
||||||
|
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
|
||||||
|
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RatingsExposingDetails : Details
|
||||||
|
{
|
||||||
|
public new UserRatings Ratings => base.Ratings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
using osu.Game.Screens.Select.Details;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneBeatmapSetOverlaySuccessRate : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(Details)
|
||||||
|
};
|
||||||
|
|
||||||
|
private GraphExposingSuccessRate successRate;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(275, 220),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Gray,
|
||||||
|
},
|
||||||
|
successRate = new GraphExposingSuccessRate
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(275, 220),
|
||||||
|
Padding = new MarginPadding(20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMetrics()
|
||||||
|
{
|
||||||
|
var firstBeatmap = createBeatmap();
|
||||||
|
var secondBeatmap = createBeatmap();
|
||||||
|
|
||||||
|
AddStep("set first set", () => successRate.Beatmap = firstBeatmap);
|
||||||
|
AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics);
|
||||||
|
|
||||||
|
AddStep("set second set", () => successRate.Beatmap = secondBeatmap);
|
||||||
|
AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics);
|
||||||
|
|
||||||
|
BeatmapInfo createBeatmap() => new BeatmapInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapMetrics
|
||||||
|
{
|
||||||
|
Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(),
|
||||||
|
Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GraphExposingSuccessRate : SuccessRate
|
||||||
|
{
|
||||||
|
public new FailRetryGraph Graph => base.Graph;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Version = "2018.712.0",
|
Version = "2018.712.0",
|
||||||
DisplayVersion = "2018.712.0",
|
DisplayVersion = "2018.712.0",
|
||||||
UpdateStream = new APIUpdateStream { Name = "lazer" },
|
UpdateStream = new APIUpdateStream { Name = OsuGameBase.CLIENT_STREAM_NAME },
|
||||||
ChangelogEntries = new List<APIChangelogEntry>
|
ChangelogEntries = new List<APIChangelogEntry>
|
||||||
{
|
{
|
||||||
new APIChangelogEntry
|
new APIChangelogEntry
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
channelManager,
|
channelManager,
|
||||||
new ChatOverlay { State = Visibility.Visible }
|
new ChatOverlay { State = { Value = Visibility.Visible } }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,24 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
int fireCount = 0;
|
||||||
|
|
||||||
Add(overlay = new TestFullscreenOverlay());
|
Add(overlay = new TestFullscreenOverlay());
|
||||||
AddStep(@"toggle", overlay.ToggleVisibility);
|
|
||||||
|
overlay.State.ValueChanged += _ => fireCount++;
|
||||||
|
|
||||||
|
AddStep(@"show", overlay.Show);
|
||||||
|
|
||||||
|
AddAssert("fire count 1", () => fireCount == 1);
|
||||||
|
|
||||||
|
AddStep(@"show again", overlay.Show);
|
||||||
|
|
||||||
|
// this logic is specific to FullscreenOverlay
|
||||||
|
AddAssert("fire count 2", () => fireCount == 2);
|
||||||
|
|
||||||
|
AddStep(@"hide", overlay.Hide);
|
||||||
|
|
||||||
|
AddAssert("fire count 3", () => fireCount == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestFullscreenOverlay : FullscreenOverlay
|
private class TestFullscreenOverlay : FullscreenOverlay
|
||||||
|
@ -5,9 +5,9 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Overlays.Profile.Sections;
|
using osu.Game.Overlays.Profile.Sections;
|
||||||
using osu.Game.Overlays.Profile.Sections.Historical;
|
using osu.Game.Overlays.Profile.Sections.Historical;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
Colour = OsuColour.Gray(0.2f)
|
Colour = OsuColour.Gray(0.2f)
|
||||||
});
|
});
|
||||||
|
|
||||||
Add(new ScrollContainer
|
Add(new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = section = new HistoricalSection(),
|
Child = section = new HistoricalSection(),
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -12,10 +13,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneUserPanel : OsuTestScene
|
public class TestSceneUserPanel : OsuTestScene
|
||||||
{
|
{
|
||||||
|
private readonly UserPanel peppy;
|
||||||
|
|
||||||
public TestSceneUserPanel()
|
public TestSceneUserPanel()
|
||||||
{
|
{
|
||||||
UserPanel flyte;
|
UserPanel flyte;
|
||||||
UserPanel peppy;
|
|
||||||
Add(new FillFlowContainer
|
Add(new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -44,13 +47,31 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
});
|
});
|
||||||
|
|
||||||
flyte.Status.Value = new UserStatusOnline();
|
flyte.Status.Value = new UserStatusOnline();
|
||||||
peppy.Status.Value = new UserStatusSoloGame();
|
peppy.Status.Value = null;
|
||||||
|
}
|
||||||
|
|
||||||
AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); });
|
[Test]
|
||||||
AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); });
|
public void UserStatusesTests()
|
||||||
AddStep(@"modding", () => { flyte.Status.Value = new UserStatusModding(); });
|
{
|
||||||
AddStep(@"offline", () => { flyte.Status.Value = new UserStatusOffline(); });
|
AddStep("online", () => { peppy.Status.Value = new UserStatusOnline(); });
|
||||||
AddStep(@"null status", () => { flyte.Status.Value = null; });
|
AddStep(@"do not disturb", () => { peppy.Status.Value = new UserStatusDoNotDisturb(); });
|
||||||
|
AddStep(@"offline", () => { peppy.Status.Value = new UserStatusOffline(); });
|
||||||
|
AddStep(@"null status", () => { peppy.Status.Value = null; });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void UserActivitiesTests()
|
||||||
|
{
|
||||||
|
Bindable<UserActivity> activity = new Bindable<UserActivity>();
|
||||||
|
|
||||||
|
peppy.Activity.BindTo(activity);
|
||||||
|
|
||||||
|
AddStep("idle", () => { activity.Value = null; });
|
||||||
|
AddStep("spectating", () => { activity.Value = new UserActivity.Spectating(); });
|
||||||
|
AddStep("solo", () => { activity.Value = new UserActivity.SoloGame(null, null); });
|
||||||
|
AddStep("choosing", () => { activity.Value = new UserActivity.ChoosingBeatmap(); });
|
||||||
|
AddStep("editing", () => { activity.Value = new UserActivity.Editing(null); });
|
||||||
|
AddStep("modding", () => { activity.Value = new UserActivity.Modding(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.Profile.Sections;
|
using osu.Game.Overlays.Profile.Sections;
|
||||||
@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(0.2f)
|
Colour = OsuColour.Gray(0.2f)
|
||||||
},
|
},
|
||||||
new ScrollContainer
|
new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new FillFlowContainer<DrawableRecentActivity>
|
Child = new FillFlowContainer<DrawableRecentActivity>
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Overlays.Profile.Sections;
|
using osu.Game.Overlays.Profile.Sections;
|
||||||
using osu.Game.Overlays.Profile.Sections.Ranks;
|
using osu.Game.Overlays.Profile.Sections.Ranks;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(0.2f)
|
Colour = OsuColour.Gray(0.2f)
|
||||||
},
|
},
|
||||||
new ScrollContainer
|
new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = ranks = new RanksSection(),
|
Child = ranks = new RanksSection(),
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Settings
|
namespace osu.Game.Tests.Visual.Settings
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneSettings : OsuTestScene
|
public class TestSceneSettingsPanel : OsuTestScene
|
||||||
{
|
{
|
||||||
private readonly SettingsPanel settings;
|
private readonly SettingsPanel settings;
|
||||||
private readonly DialogOverlay dialogOverlay;
|
private readonly DialogOverlay dialogOverlay;
|
||||||
|
|
||||||
public TestSceneSettings()
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(SettingsFooter),
|
||||||
|
typeof(SettingsOverlay),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestSceneSettingsPanel()
|
||||||
{
|
{
|
||||||
settings = new SettingsOverlay
|
settings = new SettingsOverlay
|
||||||
{
|
{
|
||||||
State = Visibility.Visible
|
State = { Value = Visibility.Visible }
|
||||||
};
|
};
|
||||||
Add(dialogOverlay = new DialogOverlay
|
Add(dialogOverlay = new DialogOverlay
|
||||||
{
|
{
|
@ -32,6 +32,10 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("all metrics", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
||||||
{
|
{
|
||||||
|
BeatmapSetInfo =
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
Version = "All Metrics",
|
Version = "All Metrics",
|
||||||
@ -50,9 +54,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
StarDifficulty = 5.3f,
|
StarDifficulty = 5.3f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,6 +63,10 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("all except source", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
||||||
{
|
{
|
||||||
|
BeatmapSetInfo =
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
Version = "All Metrics",
|
Version = "All Metrics",
|
||||||
@ -77,15 +84,18 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
StarDifficulty = 5.3f,
|
StarDifficulty = 5.3f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
AddStep("ratings", () => detailsArea.Beatmap = new DummyWorkingBeatmap(null, null)
|
||||||
{
|
{
|
||||||
|
BeatmapSetInfo =
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
BeatmapInfo =
|
BeatmapInfo =
|
||||||
{
|
{
|
||||||
Version = "Only Ratings",
|
Version = "Only Ratings",
|
||||||
@ -101,11 +111,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
OverallDifficulty = 6,
|
OverallDifficulty = 6,
|
||||||
ApproachRate = 6,
|
ApproachRate = 6,
|
||||||
},
|
},
|
||||||
StarDifficulty = 4.8f,
|
StarDifficulty = 4.8f
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Ratings = Enumerable.Range(0, 11),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -129,8 +135,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
StarDifficulty = 2.91f,
|
StarDifficulty = 2.91f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,28 +1,38 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.SongSelect
|
namespace osu.Game.Tests.Visual.SongSelect
|
||||||
{
|
{
|
||||||
[Description("PlaySongSelect beatmap details")]
|
[System.ComponentModel.Description("PlaySongSelect beatmap details")]
|
||||||
public class TestSceneBeatmapDetails : OsuTestScene
|
public class TestSceneBeatmapDetails : OsuTestScene
|
||||||
{
|
{
|
||||||
public TestSceneBeatmapDetails()
|
private BeatmapDetails details;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
{
|
{
|
||||||
BeatmapDetails details;
|
Child = details = new BeatmapDetails
|
||||||
Add(details = new BeatmapDetails
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding(150),
|
Padding = new MarginPadding(150),
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAllMetrics()
|
||||||
|
{
|
||||||
AddStep("all metrics", () => details.Beatmap = new BeatmapInfo
|
AddStep("all metrics", () => details.Beatmap = new BeatmapInfo
|
||||||
{
|
{
|
||||||
|
BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
Version = "All Metrics",
|
Version = "All Metrics",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
@ -39,14 +49,21 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
StarDifficulty = 5.3f,
|
StarDifficulty = 5.3f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAllMetricsExceptSource()
|
||||||
|
{
|
||||||
AddStep("all except source", () => details.Beatmap = new BeatmapInfo
|
AddStep("all except source", () => details.Beatmap = new BeatmapInfo
|
||||||
{
|
{
|
||||||
|
BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
Version = "All Metrics",
|
Version = "All Metrics",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
@ -62,14 +79,21 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
StarDifficulty = 5.3f,
|
StarDifficulty = 5.3f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOnlyRatings()
|
||||||
|
{
|
||||||
AddStep("ratings", () => details.Beatmap = new BeatmapInfo
|
AddStep("ratings", () => details.Beatmap = new BeatmapInfo
|
||||||
{
|
{
|
||||||
|
BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }
|
||||||
|
},
|
||||||
Version = "Only Ratings",
|
Version = "Only Ratings",
|
||||||
Metadata = new BeatmapMetadata
|
Metadata = new BeatmapMetadata
|
||||||
{
|
{
|
||||||
@ -84,12 +108,12 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
ApproachRate = 6,
|
ApproachRate = 6,
|
||||||
},
|
},
|
||||||
StarDifficulty = 4.8f,
|
StarDifficulty = 4.8f,
|
||||||
Metrics = new BeatmapMetrics
|
|
||||||
{
|
|
||||||
Ratings = Enumerable.Range(0, 11),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOnlyFailsAndRetries()
|
||||||
|
{
|
||||||
AddStep("fails retries", () => details.Beatmap = new BeatmapInfo
|
AddStep("fails retries", () => details.Beatmap = new BeatmapInfo
|
||||||
{
|
{
|
||||||
Version = "Only Retries and Fails",
|
Version = "Only Retries and Fails",
|
||||||
@ -108,11 +132,15 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
StarDifficulty = 2.91f,
|
StarDifficulty = 2.91f,
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoMetrics()
|
||||||
|
{
|
||||||
AddStep("no metrics", () => details.Beatmap = new BeatmapInfo
|
AddStep("no metrics", () => details.Beatmap = new BeatmapInfo
|
||||||
{
|
{
|
||||||
Version = "No Metrics",
|
Version = "No Metrics",
|
||||||
@ -129,10 +157,22 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
ApproachRate = 6.5f,
|
ApproachRate = 6.5f,
|
||||||
},
|
},
|
||||||
StarDifficulty = 1.97f,
|
StarDifficulty = 1.97f,
|
||||||
Metrics = new BeatmapMetrics(),
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNullBeatmap()
|
||||||
|
{
|
||||||
AddStep("null beatmap", () => details.Beatmap = null);
|
AddStep("null beatmap", () => details.Beatmap = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOnlineMetrics()
|
||||||
|
{
|
||||||
|
AddStep("online ratings/retries/fails", () => details.Beatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
OnlineBeatmapID = 162,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -7,7 +7,6 @@ using JetBrains.Annotations;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
AddStep("show", () =>
|
AddStep("show", () =>
|
||||||
{
|
{
|
||||||
infoWedge.State = Visibility.Visible;
|
infoWedge.Show();
|
||||||
infoWedge.Beatmap = Beatmap.Value;
|
infoWedge.Beatmap = Beatmap.Value;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,11 +56,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
AddWaitStep("wait for select", 3);
|
AddWaitStep("wait for select", 3);
|
||||||
|
|
||||||
AddStep("hide", () => { infoWedge.State = Visibility.Hidden; });
|
AddStep("hide", () => { infoWedge.Hide(); });
|
||||||
|
|
||||||
AddWaitStep("wait for hide", 3);
|
AddWaitStep("wait for hide", 3);
|
||||||
|
|
||||||
AddStep("show", () => { infoWedge.State = Visibility.Visible; });
|
AddStep("show", () => { infoWedge.Show(); });
|
||||||
|
|
||||||
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
||||||
{
|
{
|
||||||
|
@ -270,9 +270,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
},
|
},
|
||||||
Metrics = new BeatmapMetrics
|
Metrics = new BeatmapMetrics
|
||||||
{
|
{
|
||||||
Ratings = Enumerable.Range(0, 11),
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Taiko;
|
using osu.Game.Rulesets.Taiko;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
@ -100,8 +101,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public virtual void SetUp() =>
|
public virtual void SetUp() => Schedule(() =>
|
||||||
Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); });
|
{
|
||||||
|
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||||
|
manager?.Delete(manager.GetAllUsableBeatmapSets());
|
||||||
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDummy()
|
public void TestDummy()
|
||||||
@ -138,7 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
createSongSelect();
|
createSongSelect();
|
||||||
changeRuleset(2);
|
changeRuleset(2);
|
||||||
importForRuleset(0);
|
addRulesetImportStep(0);
|
||||||
AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
|
AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,8 +151,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
{
|
{
|
||||||
createSongSelect();
|
createSongSelect();
|
||||||
changeRuleset(2);
|
changeRuleset(2);
|
||||||
importForRuleset(2);
|
addRulesetImportStep(2);
|
||||||
importForRuleset(1);
|
addRulesetImportStep(1);
|
||||||
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2);
|
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap.RulesetID == 2);
|
||||||
|
|
||||||
changeRuleset(1);
|
changeRuleset(1);
|
||||||
@ -185,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("empty mods", () => !Mods.Value.Any());
|
AddAssert("empty mods", () => !Mods.Value.Any());
|
||||||
|
|
||||||
void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
|
void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
|
||||||
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex--;
|
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -210,7 +214,52 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("start not requested", () => !startRequested);
|
AddAssert("start not requested", () => !startRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
|
[Test]
|
||||||
|
public void TestAddNewBeatmapWhileSelectingRandom()
|
||||||
|
{
|
||||||
|
const int test_count = 10;
|
||||||
|
int beatmapChangedCount = 0;
|
||||||
|
int debounceCount = 0;
|
||||||
|
createSongSelect();
|
||||||
|
AddStep("Setup counters", () =>
|
||||||
|
{
|
||||||
|
beatmapChangedCount = 0;
|
||||||
|
debounceCount = 0;
|
||||||
|
songSelect.Carousel.SelectionChanged += _ => beatmapChangedCount++;
|
||||||
|
});
|
||||||
|
AddRepeatStep($"Create beatmaps {test_count} times", () =>
|
||||||
|
{
|
||||||
|
importForRuleset(0);
|
||||||
|
|
||||||
|
Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
|
// Wait for debounce
|
||||||
|
songSelect.Carousel.SelectNextRandom();
|
||||||
|
++debounceCount;
|
||||||
|
}, 400);
|
||||||
|
}, test_count);
|
||||||
|
|
||||||
|
AddUntilStep("Debounce limit reached", () => debounceCount == test_count);
|
||||||
|
|
||||||
|
// The selected beatmap should have changed an additional 2 times since both initially loading songselect and the first import also triggers selectionChanged
|
||||||
|
AddAssert($"Beatmap changed {test_count + 2} times", () => beatmapChangedCount == test_count + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHideSetSelectsCorrectBeatmap()
|
||||||
|
{
|
||||||
|
int? previousID = null;
|
||||||
|
createSongSelect();
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
AddStep("Move to last difficulty", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.Last()));
|
||||||
|
AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID);
|
||||||
|
AddStep("Hide first beatmap", () => manager.Hide(songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First()));
|
||||||
|
AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
|
||||||
|
|
||||||
|
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
|
||||||
|
|
||||||
private static int importId;
|
private static int importId;
|
||||||
private int getImportId() => ++importId;
|
private int getImportId() => ++importId;
|
||||||
@ -232,7 +281,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
|
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
|
||||||
|
|
||||||
for (int i = 0; i < 100; i += 10)
|
for (int i = 0; i < 100; i += 10)
|
||||||
manager.Import(createTestBeatmapSet(i, usableRulesets));
|
manager.Import(createTestBeatmapSet(i, usableRulesets)).Wait();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
/// Checks if a cursor is visible.
|
/// Checks if a cursor is visible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cursorContainer">The cursor to check.</param>
|
/// <param name="cursorContainer">The cursor to check.</param>
|
||||||
private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State == Visibility.Visible;
|
private bool checkVisible(CursorContainer cursorContainer) => cursorContainer.State.Value == Visibility.Visible;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a cursor is at the current inputmanager screen position.
|
/// Checks if a cursor is at the current inputmanager screen position.
|
||||||
@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public CursorContainer Cursor { get; }
|
public CursorContainer Cursor { get; }
|
||||||
public bool ProvidingUserCursor { get; }
|
public bool ProvidingUserCursor { get; }
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || (SmoothTransition && !ProvidingUserCursor);
|
||||||
|
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
},
|
},
|
||||||
Cursor = new TestCursorContainer
|
Cursor = new TestCursorContainer
|
||||||
{
|
{
|
||||||
State = providesUserCursor ? Visibility.Hidden : Visibility.Visible,
|
State = { Value = providesUserCursor ? Visibility.Hidden : Visibility.Visible },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
new NamedIconButton("No change", new IconButton()),
|
new NamedIconButton("No change", new IconButton()),
|
||||||
new NamedIconButton("Background colours", new ColouredIconButton()),
|
new NamedIconButton("Background colours", new ColouredIconButton()),
|
||||||
new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }),
|
new NamedIconButton("Full-width", new IconButton { Size = new Vector2(200, 30) }),
|
||||||
new NamedIconButton("Unchanging size", new IconButton(), false),
|
|
||||||
new NamedIconButton("Icon colours", new IconButton
|
new NamedIconButton("Icon colours", new IconButton
|
||||||
{
|
{
|
||||||
IconColour = Color4.Green,
|
IconColour = Color4.Green,
|
||||||
@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private class NamedIconButton : Container
|
private class NamedIconButton : Container
|
||||||
{
|
{
|
||||||
public NamedIconButton(string name, IconButton button, bool allowSizeChange = true)
|
public NamedIconButton(string name, IconButton button)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Width = 200;
|
Width = 200;
|
||||||
@ -101,13 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (allowSizeChange)
|
iconContainer.AutoSizeAxes = Axes.Both;
|
||||||
iconContainer.AutoSizeAxes = Axes.Both;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iconContainer.RelativeSizeAxes = Axes.X;
|
|
||||||
iconContainer.Height = 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.Anchor = Anchor.Centre;
|
button.Anchor = Anchor.Centre;
|
||||||
button.Origin = Anchor.Centre;
|
button.Origin = Anchor.Centre;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
@ -23,9 +22,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
};
|
};
|
||||||
Add(mc);
|
Add(mc);
|
||||||
|
|
||||||
AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
|
AddStep(@"show", () => mc.Show());
|
||||||
AddStep(@"show", () => mc.State = Visibility.Visible);
|
|
||||||
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
|
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
|
||||||
|
AddStep(@"show", () => mc.Hide());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
Content.Add(displayedCount);
|
Content.Add(displayedCount);
|
||||||
|
|
||||||
void setState(Visibility state) => AddStep(state.ToString(), () => manager.State = state);
|
void setState(Visibility state) => AddStep(state.ToString(), () => manager.State.Value = state);
|
||||||
void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected);
|
void checkProgressingCount(int expected) => AddAssert($"progressing count is {expected}", () => progressingNotifications.Count == expected);
|
||||||
|
|
||||||
manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
|
manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; };
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Cursor;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
Colour = Color4.Teal,
|
Colour = Color4.Teal,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
new ScrollContainer
|
new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = flow = new FillFlowContainer<Icon>
|
Child = flow = new FillFlowContainer<Icon>
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
var popup = new PopupDialog
|
var popup = new PopupDialog
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
State = Framework.Graphics.Containers.Visibility.Visible,
|
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
|
||||||
Icon = FontAwesome.Solid.AssistiveListeningSystems,
|
Icon = FontAwesome.Solid.AssistiveListeningSystems,
|
||||||
HeaderText = @"This is a test popup",
|
HeaderText = @"This is a test popup",
|
||||||
BodyText = "I can say lots of stuff and even wrap my words!",
|
BodyText = "I can say lots of stuff and even wrap my words!",
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
this.api = api;
|
this.api = api;
|
||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
|
|
||||||
testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu);
|
testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -92,13 +93,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public void TestUnloadAndReload()
|
public void TestUnloadAndReload()
|
||||||
{
|
{
|
||||||
var backgrounds = new List<TestUpdateableBeatmapBackgroundSprite>();
|
var backgrounds = new List<TestUpdateableBeatmapBackgroundSprite>();
|
||||||
ScrollContainer scrollContainer = null;
|
OsuScrollContainer scrollContainer = null;
|
||||||
|
|
||||||
AddStep("create backgrounds hierarchy", () =>
|
AddStep("create backgrounds hierarchy", () =>
|
||||||
{
|
{
|
||||||
FillFlowContainer backgroundFlow;
|
FillFlowContainer backgroundFlow;
|
||||||
|
|
||||||
Child = scrollContainer = new ScrollContainer
|
Child = scrollContainer = new OsuScrollContainer
|
||||||
{
|
{
|
||||||
Size = new Vector2(500),
|
Size = new Vector2(500),
|
||||||
Child = backgroundFlow = new FillFlowContainer
|
Child = backgroundFlow = new FillFlowContainer
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ScoreInfo> Scores { get; set; }
|
public List<ScoreInfo> Scores { get; set; }
|
||||||
|
|
||||||
public override string ToString() => $"{Metadata} [{Version}]";
|
public override string ToString() => $"{Metadata} [{Version}]".Trim();
|
||||||
|
|
||||||
public bool Equals(BeatmapInfo other)
|
public bool Equals(BeatmapInfo other)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -14,6 +15,7 @@ using osu.Framework.Extensions;
|
|||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
@ -72,6 +74,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
||||||
|
|
||||||
|
private readonly BeatmapUpdateQueue updateQueue;
|
||||||
|
|
||||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
|
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
|
||||||
WorkingBeatmap defaultBeatmap = null)
|
WorkingBeatmap defaultBeatmap = null)
|
||||||
: base(storage, contextFactory, new BeatmapStore(contextFactory), host)
|
: base(storage, contextFactory, new BeatmapStore(contextFactory), host)
|
||||||
@ -86,9 +90,11 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmaps = (BeatmapStore)ModelStore;
|
beatmaps = (BeatmapStore)ModelStore;
|
||||||
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
||||||
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
||||||
|
|
||||||
|
updateQueue = new BeatmapUpdateQueue(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive)
|
protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (archive != null)
|
if (archive != null)
|
||||||
beatmapSet.Beatmaps = createBeatmapDifficulties(archive);
|
beatmapSet.Beatmaps = createBeatmapDifficulties(archive);
|
||||||
@ -104,8 +110,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
validateOnlineIds(beatmapSet);
|
validateOnlineIds(beatmapSet);
|
||||||
|
|
||||||
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
return updateQueue.UpdateAsync(beatmapSet, cancellationToken);
|
||||||
fetchAndPopulateOnlineValues(b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PreImport(BeatmapSetInfo beatmapSet)
|
protected override void PreImport(BeatmapSetInfo beatmapSet)
|
||||||
@ -122,7 +127,7 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
Delete(existingOnlineId);
|
Delete(existingOnlineId);
|
||||||
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
|
beatmaps.PurgeDeletable(s => s.ID == existingOnlineId.ID);
|
||||||
Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database);
|
LogForModel(beatmapSet, $"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,10 +186,10 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
request.Success += filename =>
|
request.Success += filename =>
|
||||||
{
|
{
|
||||||
Task.Factory.StartNew(() =>
|
Task.Factory.StartNew(async () =>
|
||||||
{
|
{
|
||||||
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
||||||
Import(downloadNotification, filename);
|
await Import(downloadNotification, filename);
|
||||||
currentDownloads.Remove(request);
|
currentDownloads.Remove(request);
|
||||||
}, TaskCreationOptions.LongRunning);
|
}, TaskCreationOptions.LongRunning);
|
||||||
};
|
};
|
||||||
@ -322,6 +327,8 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>Results from the provided query.</returns>
|
/// <returns>Results from the provided query.</returns>
|
||||||
public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
|
public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>> query) => beatmaps.Beatmaps.AsNoTracking().Where(query);
|
||||||
|
|
||||||
|
protected override string HumanisedModelName => "beatmap";
|
||||||
|
|
||||||
protected override BeatmapSetInfo CreateModel(ArchiveReader reader)
|
protected override BeatmapSetInfo CreateModel(ArchiveReader reader)
|
||||||
{
|
{
|
||||||
// let's make sure there are actually .osu files to import.
|
// let's make sure there are actually .osu files to import.
|
||||||
@ -381,47 +388,6 @@ namespace osu.Game.Beatmaps
|
|||||||
return beatmapInfos;
|
return beatmapInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="beatmap">The beatmap to populate.</param>
|
|
||||||
/// <param name="force">Whether to re-query if the provided beatmap already has populated values.</param>
|
|
||||||
/// <returns>True if population was successful.</returns>
|
|
||||||
private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, bool force = false)
|
|
||||||
{
|
|
||||||
if (api?.State != APIState.Online)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null
|
|
||||||
&& beatmap.Status != BeatmapSetOnlineStatus.None && beatmap.BeatmapSet.Status != BeatmapSetOnlineStatus.None)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var req = new GetBeatmapRequest(beatmap);
|
|
||||||
|
|
||||||
req.Perform(api);
|
|
||||||
|
|
||||||
var res = req.Result;
|
|
||||||
|
|
||||||
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
|
|
||||||
|
|
||||||
beatmap.Status = res.Status;
|
|
||||||
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
|
||||||
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
|
||||||
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Log($"Failed ({e})", LoggingTarget.Database);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
|
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -455,5 +421,55 @@ namespace osu.Game.Beatmaps
|
|||||||
public override bool IsImportant => false;
|
public override bool IsImportant => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BeatmapUpdateQueue
|
||||||
|
{
|
||||||
|
private readonly IAPIProvider api;
|
||||||
|
|
||||||
|
private const int update_queue_request_concurrency = 4;
|
||||||
|
|
||||||
|
private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(update_queue_request_concurrency, nameof(BeatmapUpdateQueue));
|
||||||
|
|
||||||
|
public BeatmapUpdateQueue(IAPIProvider api)
|
||||||
|
{
|
||||||
|
this.api = api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (api?.State != APIState.Online)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
LogForModel(beatmapSet, "Performing online lookups...");
|
||||||
|
return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: expose this when we need to do individual difficulty lookups.
|
||||||
|
protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken)
|
||||||
|
=> Task.Factory.StartNew(() => update(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler);
|
||||||
|
|
||||||
|
private void update(BeatmapSetInfo set, BeatmapInfo beatmap)
|
||||||
|
{
|
||||||
|
if (api?.State != APIState.Online)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var req = new GetBeatmapRequest(beatmap);
|
||||||
|
|
||||||
|
req.Success += res =>
|
||||||
|
{
|
||||||
|
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
|
||||||
|
|
||||||
|
beatmap.Status = res.Status;
|
||||||
|
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
||||||
|
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
||||||
|
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
||||||
|
};
|
||||||
|
|
||||||
|
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap}", e); };
|
||||||
|
|
||||||
|
// intentionally blocking to limit web request concurrency
|
||||||
|
req.Perform(api);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,26 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Beatmap metrics based on acculumated online data from community plays.
|
/// Beatmap metrics based on accumulated online data from community plays.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BeatmapMetrics
|
public class BeatmapMetrics
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?).
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<int> Ratings { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Points of failure on a relative time scale (usually 0..100).
|
/// Points of failure on a relative time scale (usually 0..100).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty(@"fail")]
|
[JsonProperty(@"fail")]
|
||||||
public IEnumerable<int> Fails { get; set; }
|
public int[] Fails { get; set; } = Array.Empty<int>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Points of retry on a relative time scale (usually 0..100).
|
/// Points of retry on a relative time scale (usually 0..100).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty(@"exit")]
|
[JsonProperty(@"exit")]
|
||||||
public IEnumerable<int> Retries { get; set; }
|
public int[] Retries { get; set; } = Array.Empty<int>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,9 @@ namespace osu.Game.Beatmaps
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public BeatmapSetOnlineInfo OnlineInfo { get; set; }
|
public BeatmapSetOnlineInfo OnlineInfo { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public BeatmapSetMetrics Metrics { get; set; }
|
||||||
|
|
||||||
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0;
|
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0;
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
|
17
osu.Game/Beatmaps/BeatmapSetMetrics.cs
Normal file
17
osu.Game/Beatmaps/BeatmapSetMetrics.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps
|
||||||
|
{
|
||||||
|
public class BeatmapSetMetrics
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?).
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("ratings")]
|
||||||
|
public int[] Ratings { get; set; } = Array.Empty<int>();
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -13,6 +14,7 @@ using osu.Framework.Extensions;
|
|||||||
using osu.Framework.IO.File;
|
using osu.Framework.IO.File;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
@ -29,7 +31,7 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TModel">The model type.</typeparam>
|
/// <typeparam name="TModel">The model type.</typeparam>
|
||||||
/// <typeparam name="TFileModel">The associated file join type.</typeparam>
|
/// <typeparam name="TFileModel">The associated file join type.</typeparam>
|
||||||
public abstract class ArchiveModelManager<TModel, TFileModel> : ICanAcceptFiles
|
public abstract class ArchiveModelManager<TModel, TFileModel> : ArchiveModelManager, ICanAcceptFiles
|
||||||
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
||||||
where TFileModel : INamedFileInfo, new()
|
where TFileModel : INamedFileInfo, new()
|
||||||
{
|
{
|
||||||
@ -130,56 +132,50 @@ namespace osu.Game.Database
|
|||||||
/// This will post notifications tracking progress.
|
/// This will post notifications tracking progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="paths">One or more archive locations on disk.</param>
|
/// <param name="paths">One or more archive locations on disk.</param>
|
||||||
public void Import(params string[] paths)
|
public Task Import(params string[] paths)
|
||||||
{
|
{
|
||||||
var notification = new ProgressNotification { State = ProgressNotificationState.Active };
|
var notification = new ProgressNotification { State = ProgressNotificationState.Active };
|
||||||
|
|
||||||
PostNotification?.Invoke(notification);
|
PostNotification?.Invoke(notification);
|
||||||
Import(notification, paths);
|
|
||||||
|
return Import(notification, paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Import(ProgressNotification notification, params string[] paths)
|
protected async Task Import(ProgressNotification notification, params string[] paths)
|
||||||
{
|
{
|
||||||
notification.Progress = 0;
|
notification.Progress = 0;
|
||||||
notification.Text = "Import is initialising...";
|
notification.Text = "Import is initialising...";
|
||||||
|
|
||||||
var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}";
|
|
||||||
|
|
||||||
List<TModel> imported = new List<TModel>();
|
|
||||||
|
|
||||||
int current = 0;
|
int current = 0;
|
||||||
|
|
||||||
foreach (string path in paths)
|
var imported = new List<TModel>();
|
||||||
|
|
||||||
|
await Task.WhenAll(paths.Select(async path =>
|
||||||
{
|
{
|
||||||
if (notification.State == ProgressNotificationState.Cancelled)
|
notification.CancellationToken.ThrowIfCancellationRequested();
|
||||||
// user requested abort
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var text = "Importing ";
|
var model = await Import(path, notification.CancellationToken);
|
||||||
|
|
||||||
if (path.Length > 1)
|
lock (imported)
|
||||||
text += $"{++current} of {paths.Length} {term}s..";
|
{
|
||||||
else
|
imported.Add(model);
|
||||||
text += $"{term}..";
|
current++;
|
||||||
|
|
||||||
// only show the filename if it isn't a temporary one (as those look ugly).
|
notification.Text = $"Imported {current} of {paths.Length} {HumanisedModelName}s";
|
||||||
if (!path.Contains(Path.GetTempPath()))
|
notification.Progress = (float)current / paths.Length;
|
||||||
text += $"\n{Path.GetFileName(path)}";
|
}
|
||||||
|
}
|
||||||
notification.Text = text;
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
imported.Add(Import(path));
|
throw;
|
||||||
|
|
||||||
notification.Progress = (float)current / paths.Length;
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e = e.InnerException ?? e;
|
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})", LoggingTarget.Database);
|
||||||
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
|
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
|
|
||||||
if (imported.Count == 0)
|
if (imported.Count == 0)
|
||||||
{
|
{
|
||||||
@ -190,7 +186,7 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
notification.CompletionText = imported.Count == 1
|
notification.CompletionText = imported.Count == 1
|
||||||
? $"Imported {imported.First()}!"
|
? $"Imported {imported.First()}!"
|
||||||
: $"Imported {current} {term}s!";
|
: $"Imported {current} {HumanisedModelName}s!";
|
||||||
|
|
||||||
if (imported.Count > 0 && PresentImport != null)
|
if (imported.Count > 0 && PresentImport != null)
|
||||||
{
|
{
|
||||||
@ -210,12 +206,15 @@ namespace osu.Game.Database
|
|||||||
/// Import one <see cref="TModel"/> from the filesystem and delete the file on success.
|
/// Import one <see cref="TModel"/> from the filesystem and delete the file on success.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The archive location on disk.</param>
|
/// <param name="path">The archive location on disk.</param>
|
||||||
|
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||||
/// <returns>The imported model, if successful.</returns>
|
/// <returns>The imported model, if successful.</returns>
|
||||||
public TModel Import(string path)
|
public async Task<TModel> Import(string path, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
TModel import;
|
TModel import;
|
||||||
using (ArchiveReader reader = getReaderFrom(path))
|
using (ArchiveReader reader = getReaderFrom(path))
|
||||||
import = Import(reader);
|
import = await Import(reader, cancellationToken);
|
||||||
|
|
||||||
// We may or may not want to delete the file depending on where it is stored.
|
// We may or may not want to delete the file depending on where it is stored.
|
||||||
// e.g. reconstructing/repairing database with items from default storage.
|
// e.g. reconstructing/repairing database with items from default storage.
|
||||||
@ -228,7 +227,7 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})");
|
LogForModel(import, $@"Could not delete original file after import ({Path.GetFileName(path)})", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return import;
|
return import;
|
||||||
@ -243,23 +242,32 @@ namespace osu.Game.Database
|
|||||||
/// Import an item from an <see cref="ArchiveReader"/>.
|
/// Import an item from an <see cref="ArchiveReader"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="archive">The archive to be imported.</param>
|
/// <param name="archive">The archive to be imported.</param>
|
||||||
public TModel Import(ArchiveReader archive)
|
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||||
|
public Task<TModel> Import(ArchiveReader archive, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
TModel model = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var model = CreateModel(archive);
|
model = CreateModel(archive);
|
||||||
|
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
|
|
||||||
model.Hash = computeHash(archive);
|
model.Hash = computeHash(archive);
|
||||||
|
}
|
||||||
return Import(model, archive);
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error(e, $"Model creation of {archive.Name} failed.", LoggingTarget.Database);
|
LogForModel(model, $"Model creation of {archive.Name} failed.", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Import(model, archive, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -269,6 +277,16 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract string[] HashableFileTypes { get; }
|
protected abstract string[] HashableFileTypes { get; }
|
||||||
|
|
||||||
|
protected static void LogForModel(TModel model, string message, Exception e = null)
|
||||||
|
{
|
||||||
|
string prefix = $"[{(model?.Hash ?? "?????").Substring(0, 5)}]";
|
||||||
|
|
||||||
|
if (e != null)
|
||||||
|
Logger.Error(e, $"{prefix} {message}", LoggingTarget.Database);
|
||||||
|
else
|
||||||
|
Logger.Log($"{prefix} {message}", LoggingTarget.Database);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a SHA-2 hash from the provided archive based on file content of all files matching <see cref="HashableFileTypes"/>.
|
/// Create a SHA-2 hash from the provided archive based on file content of all files matching <see cref="HashableFileTypes"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -288,13 +306,30 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The model to be imported.</param>
|
/// <param name="item">The model to be imported.</param>
|
||||||
/// <param name="archive">An optional archive to use for model population.</param>
|
/// <param name="archive">An optional archive to use for model population.</param>
|
||||||
public TModel Import(TModel item, ArchiveReader archive = null)
|
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||||
|
public async Task<TModel> Import(TModel item, ArchiveReader archive = null, CancellationToken cancellationToken = default) => await Task.Factory.StartNew(async () =>
|
||||||
{
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
delayEvents();
|
delayEvents();
|
||||||
|
|
||||||
|
void rollback()
|
||||||
|
{
|
||||||
|
if (!Delete(item))
|
||||||
|
{
|
||||||
|
// We may have not yet added the model to the underlying table, but should still clean up files.
|
||||||
|
LogForModel(item, "Dereferencing files for incomplete import.");
|
||||||
|
Files.Dereference(item.Files.Select(f => f.FileInfo).ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Log($"Importing {item}...", LoggingTarget.Database);
|
LogForModel(item, "Beginning import...");
|
||||||
|
|
||||||
|
item.Files = archive != null ? createFileInfos(archive, Files) : new List<TFileModel>();
|
||||||
|
|
||||||
|
await Populate(item, archive, cancellationToken);
|
||||||
|
|
||||||
using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes.
|
using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes.
|
||||||
{
|
{
|
||||||
@ -302,11 +337,6 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
|
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
|
||||||
|
|
||||||
if (archive != null)
|
|
||||||
item.Files = createFileInfos(archive, Files);
|
|
||||||
|
|
||||||
Populate(item, archive);
|
|
||||||
|
|
||||||
var existing = CheckForExisting(item);
|
var existing = CheckForExisting(item);
|
||||||
|
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
@ -314,15 +344,17 @@ namespace osu.Game.Database
|
|||||||
if (CanUndelete(existing, item))
|
if (CanUndelete(existing, item))
|
||||||
{
|
{
|
||||||
Undelete(existing);
|
Undelete(existing);
|
||||||
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
|
LogForModel(item, $"Found existing {HumanisedModelName} for {item} (ID {existing.ID}) – skipping import.");
|
||||||
handleEvent(() => ItemAdded?.Invoke(existing, true));
|
handleEvent(() => ItemAdded?.Invoke(existing, true));
|
||||||
|
|
||||||
|
// existing item will be used; rollback new import and exit early.
|
||||||
|
rollback();
|
||||||
|
flushEvents(true);
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
Delete(existing);
|
||||||
Delete(existing);
|
ModelStore.PurgeDeletable(s => s.ID == existing.ID);
|
||||||
ModelStore.PurgeDeletable(s => s.ID == existing.ID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PreImport(item);
|
PreImport(item);
|
||||||
@ -337,21 +369,21 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database);
|
LogForModel(item, "Import successfully completed!");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database);
|
if (!(e is TaskCanceledException))
|
||||||
item = null;
|
LogForModel(item, "Database import or population failed and has been rolled back.", e);
|
||||||
}
|
|
||||||
finally
|
rollback();
|
||||||
{
|
flushEvents(false);
|
||||||
// we only want to flush events after we've confirmed the write context didn't have any errors.
|
throw;
|
||||||
flushEvents(item != null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flushEvents(true);
|
||||||
return item;
|
return item;
|
||||||
}
|
}, cancellationToken, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform an update of the specified item.
|
/// Perform an update of the specified item.
|
||||||
@ -392,7 +424,8 @@ namespace osu.Game.Database
|
|||||||
var notification = new ProgressNotification
|
var notification = new ProgressNotification
|
||||||
{
|
{
|
||||||
Progress = 0,
|
Progress = 0,
|
||||||
CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!",
|
Text = $"Preparing to delete all {HumanisedModelName}s...",
|
||||||
|
CompletionText = $"Deleted all {HumanisedModelName}s!",
|
||||||
State = ProgressNotificationState.Active,
|
State = ProgressNotificationState.Active,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -409,7 +442,7 @@ namespace osu.Game.Database
|
|||||||
// user requested abort
|
// user requested abort
|
||||||
return;
|
return;
|
||||||
|
|
||||||
notification.Text = $"Deleting ({++i} of {items.Count})";
|
notification.Text = $"Deleting {HumanisedModelName}s ({++i} of {items.Count})";
|
||||||
|
|
||||||
Delete(b);
|
Delete(b);
|
||||||
|
|
||||||
@ -533,7 +566,7 @@ namespace osu.Game.Database
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
|
return Task.Run(async () => await Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -552,9 +585,8 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The model to populate.</param>
|
/// <param name="model">The model to populate.</param>
|
||||||
/// <param name="archive">The archive to use as a reference for population. May be null.</param>
|
/// <param name="archive">The archive to use as a reference for population. May be null.</param>
|
||||||
protected virtual void Populate(TModel model, [CanBeNull] ArchiveReader archive)
|
/// <param name="cancellationToken">An optional cancellation token.</param>
|
||||||
{
|
protected virtual Task Populate(TModel model, [CanBeNull] ArchiveReader archive, CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform any final actions before the import to database executes.
|
/// Perform any final actions before the import to database executes.
|
||||||
@ -582,6 +614,8 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
private DbSet<TModel> queryModel() => ContextFactory.Get().Set<TModel>();
|
private DbSet<TModel> queryModel() => ContextFactory.Get().Set<TModel>();
|
||||||
|
|
||||||
|
protected virtual string HumanisedModelName => $"{typeof(TModel).Name.Replace("Info", "").ToLower()}";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.
|
/// Creates an <see cref="ArchiveReader"/> from a valid storage path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -599,4 +633,18 @@ namespace osu.Game.Database
|
|||||||
throw new InvalidFormatException($"{path} is not a valid archive");
|
throw new InvalidFormatException($"{path} is not a valid archive");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class ArchiveModelManager
|
||||||
|
{
|
||||||
|
private const int import_queue_request_concurrency = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A singleton scheduler shared by all <see cref="ArchiveModelManager{TModel,TFileModel}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly.
|
||||||
|
/// It is mainly being used as a queue mechanism for large imports.
|
||||||
|
/// </remarks>
|
||||||
|
protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace osu.Game.Database
|
namespace osu.Game.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -12,7 +14,7 @@ namespace osu.Game.Database
|
|||||||
/// Import the specified paths.
|
/// Import the specified paths.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="paths">The files which should be imported.</param>
|
/// <param name="paths">The files which should be imported.</param>
|
||||||
void Import(params string[] paths);
|
Task Import(params string[] paths);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An array of accepted file extensions (in the standard format of ".abc").
|
/// An array of accepted file extensions (in the standard format of ".abc").
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
protected void BeginConfirm()
|
protected void BeginConfirm()
|
||||||
{
|
{
|
||||||
if (confirming || !AllowMultipleFires && fired) return;
|
if (confirming || (!AllowMultipleFires && fired)) return;
|
||||||
|
|
||||||
confirming = true;
|
confirming = true;
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
|
samplePopIn = audio.Samples.Get(@"UI/overlay-pop-in");
|
||||||
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
|
samplePopOut = audio.Samples.Get(@"UI/overlay-pop-out");
|
||||||
|
|
||||||
StateChanged += onStateChanged;
|
State.ValueChanged += onStateChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -70,7 +70,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
{
|
{
|
||||||
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
||||||
{
|
{
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case GlobalAction.Back:
|
case GlobalAction.Back:
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.Select:
|
case GlobalAction.Select:
|
||||||
@ -94,9 +94,9 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
public bool OnReleased(GlobalAction action) => false;
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
|
|
||||||
private void onStateChanged(Visibility visibility)
|
private void onStateChanged(ValueChangedEvent<Visibility> state)
|
||||||
{
|
{
|
||||||
switch (visibility)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
case Visibility.Visible:
|
case Visibility.Visible:
|
||||||
if (OverlayActivationMode.Value != OverlayActivation.Disabled)
|
if (OverlayActivationMode.Value != OverlayActivation.Disabled)
|
||||||
@ -105,7 +105,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this);
|
if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
{
|
{
|
||||||
public class OsuScrollContainer : ScrollContainer
|
public class OsuScrollContainer : ScrollContainer<Drawable>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows controlling the scroll bar from any position in the container using the right mouse button.
|
/// Allows controlling the scroll bar from any position in the container using the right mouse button.
|
||||||
@ -28,6 +33,11 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
|
protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
|
||||||
|
|
||||||
|
public OsuScrollContainer(Direction scrollDirection = Direction.Vertical)
|
||||||
|
: base(scrollDirection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
if (shouldPerformRightMouseScroll(e))
|
if (shouldPerformRightMouseScroll(e))
|
||||||
@ -71,5 +81,87 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
return base.OnDragEnd(e);
|
return base.OnDragEnd(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction);
|
||||||
|
|
||||||
|
protected class OsuScrollbar : ScrollbarContainer
|
||||||
|
{
|
||||||
|
private const float dim_size = 10;
|
||||||
|
|
||||||
|
private Color4 hoverColour;
|
||||||
|
private Color4 defaultColour;
|
||||||
|
private Color4 highlightColour;
|
||||||
|
|
||||||
|
private readonly Box box;
|
||||||
|
|
||||||
|
public OsuScrollbar(Direction scrollDir)
|
||||||
|
: base(scrollDir)
|
||||||
|
{
|
||||||
|
Blending = BlendingMode.Additive;
|
||||||
|
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
const float margin = 3;
|
||||||
|
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Left = scrollDir == Direction.Vertical ? margin : 0,
|
||||||
|
Right = scrollDir == Direction.Vertical ? margin : 0,
|
||||||
|
Top = scrollDir == Direction.Horizontal ? margin : 0,
|
||||||
|
Bottom = scrollDir == Direction.Horizontal ? margin : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
Child = box = new Box { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
ResizeTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Colour = defaultColour = colours.Gray8;
|
||||||
|
hoverColour = colours.GrayF;
|
||||||
|
highlightColour = colours.Green;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ResizeTo(float val, int duration = 0, Easing easing = Easing.None)
|
||||||
|
{
|
||||||
|
Vector2 size = new Vector2(dim_size)
|
||||||
|
{
|
||||||
|
[(int)ScrollDirection] = val
|
||||||
|
};
|
||||||
|
this.ResizeTo(size, duration, easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
this.FadeColour(hoverColour, 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
this.FadeColour(defaultColour, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
if (!base.OnMouseDown(e)) return false;
|
||||||
|
|
||||||
|
//note that we are changing the colour of the box here as to not interfere with the hover effect.
|
||||||
|
box.FadeColour(highlightColour, 100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button != MouseButton.Left) return false;
|
||||||
|
|
||||||
|
box.FadeColour(Color4.White, 100);
|
||||||
|
|
||||||
|
return base.OnMouseUp(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
where T : Drawable
|
where T : Drawable
|
||||||
{
|
{
|
||||||
private Drawable expandableHeader, fixedHeader, footer, headerBackground;
|
private Drawable expandableHeader, fixedHeader, footer, headerBackground;
|
||||||
private readonly ScrollContainer scrollContainer;
|
private readonly OsuScrollContainer scrollContainer;
|
||||||
private readonly Container headerBackgroundContainer;
|
private readonly Container headerBackgroundContainer;
|
||||||
private readonly FlowContainer<T> scrollContentContainer;
|
private readonly FlowContainer<T> scrollContentContainer;
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
public SectionsContainer()
|
public SectionsContainer()
|
||||||
{
|
{
|
||||||
AddInternal(scrollContainer = new ScrollContainer
|
AddInternal(scrollContainer = new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
foreach (var w in wavesContainer.Children)
|
foreach (var w in wavesContainer.Children)
|
||||||
w.State = Visibility.Visible;
|
w.Show();
|
||||||
|
|
||||||
this.FadeIn(100, Easing.OutQuint);
|
this.FadeIn(100, Easing.OutQuint);
|
||||||
contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint);
|
contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint);
|
||||||
@ -117,7 +117,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In);
|
contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, Easing.In);
|
||||||
|
|
||||||
foreach (var w in wavesContainer.Children)
|
foreach (var w in wavesContainer.Children)
|
||||||
w.State = Visibility.Hidden;
|
w.Hide();
|
||||||
|
|
||||||
this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
|
this.FadeOut(DISAPPEAR_DURATION, Easing.InQuint);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
{
|
{
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
Cursor = new MenuCursor { State = Visibility.Hidden },
|
Cursor = new MenuCursor { State = { Value = Visibility.Hidden } },
|
||||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,10 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Hide() => State = Visibility.Hidden;
|
||||||
|
|
||||||
|
public override void Show() => State = Visibility.Visible;
|
||||||
|
|
||||||
public BreadcrumbTabItem(T value)
|
public BreadcrumbTabItem(T value)
|
||||||
: base(value)
|
: base(value)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
public class IconButton : OsuAnimatedButton
|
public class IconButton : OsuAnimatedButton
|
||||||
{
|
{
|
||||||
public const float BUTTON_SIZE = 30;
|
public const float DEFAULT_BUTTON_SIZE = 30;
|
||||||
|
|
||||||
private Color4? iconColour;
|
private Color4? iconColour;
|
||||||
|
|
||||||
@ -57,26 +57,11 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
set => icon.Scale = value;
|
set => icon.Scale = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of the <see cref="IconButton"/> while it is not being pressed.
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 ButtonSize
|
|
||||||
{
|
|
||||||
get => Content.Size;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Content.RelativeSizeAxes = Axes.None;
|
|
||||||
Content.AutoSizeAxes = Axes.None;
|
|
||||||
Content.Size = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly SpriteIcon icon;
|
private readonly SpriteIcon icon;
|
||||||
|
|
||||||
public IconButton()
|
public IconButton()
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
Size = new Vector2(DEFAULT_BUTTON_SIZE);
|
||||||
ButtonSize = new Vector2(BUTTON_SIZE);
|
|
||||||
|
|
||||||
Add(icon = new SpriteIcon
|
Add(icon = new SpriteIcon
|
||||||
{
|
{
|
||||||
|
@ -19,14 +19,14 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
public class OsuAnimatedButton : OsuClickableContainer
|
public class OsuAnimatedButton : OsuClickableContainer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The colour that should be flashed when the <see cref="IconButton"/> is clicked.
|
/// The colour that should be flashed when the <see cref="OsuAnimatedButton"/> is clicked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Color4 FlashColour = Color4.White.Opacity(0.3f);
|
protected Color4 FlashColour = Color4.White.Opacity(0.3f);
|
||||||
|
|
||||||
private Color4 hoverColour = Color4.White.Opacity(0.1f);
|
private Color4 hoverColour = Color4.White.Opacity(0.1f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The background colour of the <see cref="IconButton"/> while it is hovered.
|
/// The background colour of the <see cref="OsuAnimatedButton"/> while it is hovered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Color4 HoverColour
|
protected Color4 HoverColour
|
||||||
{
|
{
|
||||||
|
@ -17,11 +17,11 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A button with added default sound effects.
|
/// A button with added default sound effects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class OsuButton : Button
|
public class OsuButton : Button
|
||||||
{
|
{
|
||||||
private Box hover;
|
private Box hover;
|
||||||
|
|
||||||
protected OsuButton()
|
public OsuButton()
|
||||||
{
|
{
|
||||||
Height = 40;
|
Height = 40;
|
||||||
|
|
||||||
|
@ -6,10 +6,9 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Containers;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
@ -33,27 +32,26 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
public string LabelText
|
public string LabelText
|
||||||
{
|
{
|
||||||
get => labelSpriteText?.Text;
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (labelSpriteText != null)
|
if (labelText != null)
|
||||||
labelSpriteText.Text = value;
|
labelText.Text = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MarginPadding LabelPadding
|
public MarginPadding LabelPadding
|
||||||
{
|
{
|
||||||
get => labelSpriteText?.Padding ?? new MarginPadding();
|
get => labelText?.Padding ?? new MarginPadding();
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (labelSpriteText != null)
|
if (labelText != null)
|
||||||
labelSpriteText.Padding = value;
|
labelText.Padding = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly Nub Nub;
|
protected readonly Nub Nub;
|
||||||
|
|
||||||
private readonly SpriteText labelSpriteText;
|
private readonly OsuTextFlowContainer labelText;
|
||||||
private SampleChannel sampleChecked;
|
private SampleChannel sampleChecked;
|
||||||
private SampleChannel sampleUnchecked;
|
private SampleChannel sampleUnchecked;
|
||||||
|
|
||||||
@ -62,24 +60,28 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
const float nub_padding = 5;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
labelSpriteText = new OsuSpriteText(),
|
labelText = new OsuTextFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding }
|
||||||
|
},
|
||||||
Nub = new Nub
|
Nub = new Nub
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Margin = new MarginPadding { Right = 5 },
|
Margin = new MarginPadding { Right = nub_padding },
|
||||||
},
|
},
|
||||||
new HoverClickSounds()
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
|
|
||||||
Nub.Current.BindTo(Current);
|
Nub.Current.BindTo(Current);
|
||||||
|
|
||||||
Current.DisabledChanged += disabled =>
|
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
|
||||||
{
|
|
||||||
labelSpriteText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.9f,
|
Alpha = 0.9f,
|
||||||
},
|
},
|
||||||
new LoadingAnimation { State = Visibility.Visible }
|
new LoadingAnimation { State = { Value = Visibility.Visible } }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
@ -27,6 +30,13 @@ namespace osu.Game.IO
|
|||||||
Store = new StorageBackedResourceStore(Storage);
|
Store = new StorageBackedResourceStore(Storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a lookup query on available <see cref="FileInfo"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <returns>Results from the provided query.</returns>
|
||||||
|
public IEnumerable<FileInfo> QueryFiles(Expression<Func<FileInfo, bool>> query) => ContextFactory.Get().Set<FileInfo>().AsNoTracking().Where(f => f.ReferenceCount > 0).Where(query);
|
||||||
|
|
||||||
public FileInfo Add(Stream data, bool reference = true)
|
public FileInfo Add(Stream data, bool reference = true)
|
||||||
{
|
{
|
||||||
using (var usage = ContextFactory.GetForWrite())
|
using (var usage = ContextFactory.GetForWrite())
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.IPC
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (importer.HandledExtensions.Contains(Path.GetExtension(path)?.ToLowerInvariant()))
|
if (importer.HandledExtensions.Contains(Path.GetExtension(path)?.ToLowerInvariant()))
|
||||||
importer.Import(path);
|
await importer.Import(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,9 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public Bindable<User> LocalUser { get; } = new Bindable<User>(createGuestUser());
|
public Bindable<User> LocalUser { get; } = new Bindable<User>(createGuestUser());
|
||||||
|
|
||||||
protected bool HasLogin => authentication.Token.Value != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password);
|
public Bindable<UserActivity> Activity { get; } = new Bindable<UserActivity>();
|
||||||
|
|
||||||
|
protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));
|
||||||
|
|
||||||
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
@ -55,6 +57,12 @@ namespace osu.Game.Online.API
|
|||||||
authentication.TokenString = config.Get<string>(OsuSetting.Token);
|
authentication.TokenString = config.Get<string>(OsuSetting.Token);
|
||||||
authentication.Token.ValueChanged += onTokenChanged;
|
authentication.Token.ValueChanged += onTokenChanged;
|
||||||
|
|
||||||
|
LocalUser.BindValueChanged(u =>
|
||||||
|
{
|
||||||
|
u.OldValue?.Activity.UnbindFrom(Activity);
|
||||||
|
u.NewValue.Activity.BindTo(Activity);
|
||||||
|
}, true);
|
||||||
|
|
||||||
var thread = new Thread(run)
|
var thread = new Thread(run)
|
||||||
{
|
{
|
||||||
Name = "APIAccess",
|
Name = "APIAccess",
|
||||||
@ -254,8 +262,9 @@ namespace osu.Game.Online.API
|
|||||||
handleWebException(we);
|
handleWebException(we);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Logger.Error(ex, "Error occurred while handling an API request.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,18 @@ namespace osu.Game.Online.API
|
|||||||
{
|
{
|
||||||
private string filename;
|
private string filename;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to set the extension of the file returned by this request.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual string FileExtension { get; } = @".tmp";
|
||||||
|
|
||||||
protected override WebRequest CreateWebRequest()
|
protected override WebRequest CreateWebRequest()
|
||||||
{
|
{
|
||||||
var request = new FileWebRequest(filename = Path.GetTempFileName(), Uri);
|
var file = Path.GetTempFileName();
|
||||||
|
|
||||||
|
File.Move(file, filename = Path.ChangeExtension(file, FileExtension));
|
||||||
|
|
||||||
|
var request = new FileWebRequest(filename, Uri);
|
||||||
request.DownloadProgress += request_Progress;
|
request.DownloadProgress += request_Progress;
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ namespace osu.Game.Online.API
|
|||||||
Id = 1001,
|
Id = 1001,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
public Bindable<UserActivity> Activity { get; } = new Bindable<UserActivity>();
|
||||||
|
|
||||||
public bool IsLoggedIn => true;
|
public bool IsLoggedIn => true;
|
||||||
|
|
||||||
public string ProvidedUsername => LocalUser.Value.Username;
|
public string ProvidedUsername => LocalUser.Value.Username;
|
||||||
@ -41,6 +43,15 @@ namespace osu.Game.Online.API
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DummyAPIAccess()
|
||||||
|
{
|
||||||
|
LocalUser.BindValueChanged(u =>
|
||||||
|
{
|
||||||
|
u.OldValue?.Activity.UnbindFrom(Activity);
|
||||||
|
u.NewValue.Activity.BindTo(Activity);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Queue(APIRequest request)
|
public virtual void Queue(APIRequest request)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,11 @@ namespace osu.Game.Online.API
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Bindable<User> LocalUser { get; }
|
Bindable<User> LocalUser { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current user's activity.
|
||||||
|
/// </summary>
|
||||||
|
Bindable<UserActivity> Activity { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether the local user is logged in.
|
/// Returns whether the local user is logged in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
// 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 osu.Game.Online.API.Requests.Responses;
|
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
|
||||||
{
|
|
||||||
public class GetBeatmapDetailsRequest : APIRequest<APIBeatmapMetrics>
|
|
||||||
{
|
|
||||||
private readonly BeatmapInfo beatmap;
|
|
||||||
|
|
||||||
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
|
|
||||||
{
|
|
||||||
this.beatmap = beatmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}";
|
|
||||||
}
|
|
||||||
}
|
|
@ -57,6 +57,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
[JsonProperty(@"version")]
|
[JsonProperty(@"version")]
|
||||||
private string version { get; set; }
|
private string version { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"failtimes")]
|
||||||
|
private BeatmapMetrics metrics { get; set; }
|
||||||
|
|
||||||
public BeatmapInfo ToBeatmap(RulesetStore rulesets)
|
public BeatmapInfo ToBeatmap(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
var set = BeatmapSet?.ToBeatmapSet(rulesets);
|
var set = BeatmapSet?.ToBeatmapSet(rulesets);
|
||||||
@ -70,6 +73,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
Version = version,
|
Version = version,
|
||||||
Status = Status,
|
Status = Status,
|
||||||
BeatmapSet = set,
|
BeatmapSet = set,
|
||||||
|
Metrics = metrics,
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
DrainRate = drainRate,
|
DrainRate = drainRate,
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
// 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.Game.Beatmaps;
|
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests.Responses
|
|
||||||
{
|
|
||||||
public class APIBeatmapMetrics : BeatmapMetrics
|
|
||||||
{
|
|
||||||
//the online API returns some metrics as a nested object.
|
|
||||||
[JsonProperty(@"failtimes")]
|
|
||||||
private BeatmapMetrics failTimes
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Fails = value.Fails;
|
|
||||||
Retries = value.Retries;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//and other metrics in the beatmap set.
|
|
||||||
[JsonProperty(@"beatmapset")]
|
|
||||||
private BeatmapMetrics beatmapSet
|
|
||||||
{
|
|
||||||
set => Ratings = value.Ratings;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -54,6 +54,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
[JsonProperty(@"last_updated")]
|
[JsonProperty(@"last_updated")]
|
||||||
private DateTimeOffset lastUpdated { get; set; }
|
private DateTimeOffset lastUpdated { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"ratings")]
|
||||||
|
private int[] ratings { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"user_id")]
|
[JsonProperty(@"user_id")]
|
||||||
private long creatorId
|
private long creatorId
|
||||||
{
|
{
|
||||||
@ -70,6 +73,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
OnlineBeatmapSetID = OnlineBeatmapSetID,
|
OnlineBeatmapSetID = OnlineBeatmapSetID,
|
||||||
Metadata = this,
|
Metadata = this,
|
||||||
Status = Status,
|
Status = Status,
|
||||||
|
Metrics = ratings == null ? null : new BeatmapSetMetrics { Ratings = ratings },
|
||||||
OnlineInfo = new BeatmapSetOnlineInfo
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
{
|
{
|
||||||
Covers = covers,
|
Covers = covers,
|
||||||
|
@ -31,9 +31,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
public List<APIChangelogEntry> ChangelogEntries { get; set; }
|
public List<APIChangelogEntry> ChangelogEntries { get; set; }
|
||||||
|
|
||||||
[JsonProperty("versions")]
|
[JsonProperty("versions")]
|
||||||
public VersionNatigation Versions { get; set; }
|
public VersionNavigation Versions { get; set; }
|
||||||
|
|
||||||
public class VersionNatigation
|
public class VersionNavigation
|
||||||
{
|
{
|
||||||
[JsonProperty("next")]
|
[JsonProperty("next")]
|
||||||
public APIChangelogBuild Next { get; set; }
|
public APIChangelogBuild Next { get; set; }
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
case "cuttingedge":
|
case "cuttingedge":
|
||||||
return new Color4(238, 170, 0, 255);
|
return new Color4(238, 170, 0, 255);
|
||||||
|
|
||||||
case "lazer":
|
case OsuGameBase.CLIENT_STREAM_NAME:
|
||||||
return new Color4(237, 18, 33, 255);
|
return new Color4(237, 18, 33, 255);
|
||||||
|
|
||||||
case "web":
|
case "web":
|
||||||
|
@ -30,7 +30,10 @@ namespace osu.Game.Online.API.Requests
|
|||||||
req.ContentType = "application/json";
|
req.ContentType = "application/json";
|
||||||
req.Method = HttpMethod.Put;
|
req.Method = HttpMethod.Put;
|
||||||
|
|
||||||
req.AddRaw(JsonConvert.SerializeObject(scoreInfo));
|
req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||||
|
}));
|
||||||
|
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Online.Chat
|
|||||||
if (displayText.Length == 0 || linkText.Length == 0) continue;
|
if (displayText.Length == 0 || linkText.Length == 0) continue;
|
||||||
|
|
||||||
// Check for encapsulated links
|
// Check for encapsulated links
|
||||||
if (result.Links.Find(l => l.Index <= index && l.Index + l.Length >= index + m.Length || index <= l.Index && index + m.Length >= l.Index + l.Length) == null)
|
if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null)
|
||||||
{
|
{
|
||||||
result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText);
|
result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText);
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Online.Leaderboards
|
|||||||
{
|
{
|
||||||
private const double fade_duration = 300;
|
private const double fade_duration = 300;
|
||||||
|
|
||||||
private readonly ScrollContainer scrollContainer;
|
private readonly OsuScrollContainer scrollContainer;
|
||||||
private readonly Container placeholderContainer;
|
private readonly Container placeholderContainer;
|
||||||
|
|
||||||
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
||||||
|
@ -132,12 +132,12 @@ namespace osu.Game
|
|||||||
public void CloseAllOverlays(bool hideToolbarElements = true)
|
public void CloseAllOverlays(bool hideToolbarElements = true)
|
||||||
{
|
{
|
||||||
foreach (var overlay in overlays)
|
foreach (var overlay in overlays)
|
||||||
overlay.State = Visibility.Hidden;
|
overlay.Hide();
|
||||||
|
|
||||||
if (hideToolbarElements)
|
if (hideToolbarElements)
|
||||||
{
|
{
|
||||||
foreach (var overlay in toolbarElements)
|
foreach (var overlay in toolbarElements)
|
||||||
overlay.State = Visibility.Hidden;
|
overlay.Hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ namespace osu.Game
|
|||||||
configSkin.ValueChanged += skinId => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == skinId.NewValue) ?? SkinInfo.Default;
|
configSkin.ValueChanged += skinId => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == skinId.NewValue) ?? SkinInfo.Default;
|
||||||
configSkin.TriggerChange();
|
configSkin.TriggerChange();
|
||||||
|
|
||||||
LocalConfig.BindWith(OsuSetting.VolumeInactive, inactiveVolumeAdjust);
|
LocalConfig.BindWith(OsuSetting.VolumeInactive, userInactiveVolume);
|
||||||
|
|
||||||
IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true);
|
IsActive.BindValueChanged(active => updateActiveState(active.NewValue), true);
|
||||||
}
|
}
|
||||||
@ -461,7 +461,7 @@ namespace osu.Game
|
|||||||
loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true);
|
loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true);
|
||||||
loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add);
|
loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add);
|
||||||
|
|
||||||
chatOverlay.StateChanged += state => channelManager.HighPollRate.Value = state == Visibility.Visible;
|
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
|
||||||
|
|
||||||
Add(externalLinkOpener = new ExternalLinkOpener());
|
Add(externalLinkOpener = new ExternalLinkOpener());
|
||||||
|
|
||||||
@ -470,9 +470,9 @@ namespace osu.Game
|
|||||||
|
|
||||||
foreach (var overlay in singleDisplaySideOverlays)
|
foreach (var overlay in singleDisplaySideOverlays)
|
||||||
{
|
{
|
||||||
overlay.StateChanged += state =>
|
overlay.State.ValueChanged += state =>
|
||||||
{
|
{
|
||||||
if (state == Visibility.Hidden) return;
|
if (state.NewValue == Visibility.Hidden) return;
|
||||||
|
|
||||||
singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
|
singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
|
||||||
};
|
};
|
||||||
@ -484,9 +484,9 @@ namespace osu.Game
|
|||||||
|
|
||||||
foreach (var overlay in informationalOverlays)
|
foreach (var overlay in informationalOverlays)
|
||||||
{
|
{
|
||||||
overlay.StateChanged += state =>
|
overlay.State.ValueChanged += state =>
|
||||||
{
|
{
|
||||||
if (state == Visibility.Hidden) return;
|
if (state.NewValue == Visibility.Hidden) return;
|
||||||
|
|
||||||
informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
|
informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
|
||||||
};
|
};
|
||||||
@ -498,12 +498,12 @@ namespace osu.Game
|
|||||||
|
|
||||||
foreach (var overlay in singleDisplayOverlays)
|
foreach (var overlay in singleDisplayOverlays)
|
||||||
{
|
{
|
||||||
overlay.StateChanged += state =>
|
overlay.State.ValueChanged += state =>
|
||||||
{
|
{
|
||||||
// informational overlays should be dismissed on a show or hide of a full overlay.
|
// informational overlays should be dismissed on a show or hide of a full overlay.
|
||||||
informationalOverlays.ForEach(o => o.Hide());
|
informationalOverlays.ForEach(o => o.Hide());
|
||||||
|
|
||||||
if (state == Visibility.Hidden) return;
|
if (state.NewValue == Visibility.Hidden) return;
|
||||||
|
|
||||||
singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
|
singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide());
|
||||||
};
|
};
|
||||||
@ -518,16 +518,16 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
float offset = 0;
|
float offset = 0;
|
||||||
|
|
||||||
if (settings.State == Visibility.Visible)
|
if (settings.State.Value == Visibility.Visible)
|
||||||
offset += ToolbarButton.WIDTH / 2;
|
offset += ToolbarButton.WIDTH / 2;
|
||||||
if (notifications.State == Visibility.Visible)
|
if (notifications.State.Value == Visibility.Visible)
|
||||||
offset -= ToolbarButton.WIDTH / 2;
|
offset -= ToolbarButton.WIDTH / 2;
|
||||||
|
|
||||||
screenContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
|
screenContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.StateChanged += _ => updateScreenOffset();
|
settings.State.ValueChanged += _ => updateScreenOffset();
|
||||||
notifications.StateChanged += _ => updateScreenOffset();
|
notifications.State.ValueChanged += _ => updateScreenOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GameIdleTracker : IdleTracker
|
public class GameIdleTracker : IdleTracker
|
||||||
@ -686,16 +686,28 @@ namespace osu.Game
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly BindableDouble inactiveVolumeAdjust = new BindableDouble();
|
#region Inactive audio dimming
|
||||||
|
|
||||||
|
private readonly BindableDouble userInactiveVolume = new BindableDouble();
|
||||||
|
|
||||||
|
private readonly BindableDouble inactiveVolumeFade = new BindableDouble();
|
||||||
|
|
||||||
private void updateActiveState(bool isActive)
|
private void updateActiveState(bool isActive)
|
||||||
{
|
{
|
||||||
if (isActive)
|
if (isActive)
|
||||||
Audio.RemoveAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust);
|
{
|
||||||
|
this.TransformBindableTo(inactiveVolumeFade, 1, 500, Easing.OutQuint)
|
||||||
|
.Finally(_ => Audio.RemoveAdjustment(AdjustableProperty.Volume, inactiveVolumeFade)); //wait for the transition to finish to remove the inactive audio adjustment
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeAdjust);
|
{
|
||||||
|
Audio.AddAdjustment(AdjustableProperty.Volume, inactiveVolumeFade);
|
||||||
|
this.TransformBindableTo(inactiveVolumeFade, userInactiveVolume.Value, 1500, Easing.OutSine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public bool OnReleased(GlobalAction action) => false;
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
|
|
||||||
private Container overlayContent;
|
private Container overlayContent;
|
||||||
@ -768,7 +780,7 @@ namespace osu.Game
|
|||||||
if (newOsuScreen.HideOverlaysOnEnter)
|
if (newOsuScreen.HideOverlaysOnEnter)
|
||||||
CloseAllOverlays();
|
CloseAllOverlays();
|
||||||
else
|
else
|
||||||
Toolbar.State = Visibility.Visible;
|
Toolbar.Show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -44,6 +45,8 @@ namespace osu.Game
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class OsuGameBase : Framework.Game, ICanAcceptFiles
|
public class OsuGameBase : Framework.Game, ICanAcceptFiles
|
||||||
{
|
{
|
||||||
|
public const string CLIENT_STREAM_NAME = "lazer";
|
||||||
|
|
||||||
protected OsuConfigManager LocalConfig;
|
protected OsuConfigManager LocalConfig;
|
||||||
|
|
||||||
protected BeatmapManager BeatmapManager;
|
protected BeatmapManager BeatmapManager;
|
||||||
@ -268,13 +271,13 @@ namespace osu.Game
|
|||||||
|
|
||||||
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
|
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
|
||||||
|
|
||||||
public void Import(params string[] paths)
|
public async Task Import(params string[] paths)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
|
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
|
||||||
|
|
||||||
foreach (var importer in fileImporters)
|
foreach (var importer in fileImporters)
|
||||||
if (importer.HandledExtensions.Contains(extension))
|
if (importer.HandledExtensions.Contains(extension))
|
||||||
importer.Import(paths);
|
await importer.Import(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();
|
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();
|
||||||
|
@ -109,7 +109,7 @@ namespace osu.Game.Overlays
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case APIState.Online:
|
case APIState.Online:
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
private void updateDisplay()
|
private void updateDisplay()
|
||||||
{
|
{
|
||||||
bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-";
|
bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToString(@"0.##") ?? "-";
|
||||||
|
|
||||||
if (beatmap == null)
|
if (beatmap == null)
|
||||||
{
|
{
|
||||||
|
@ -16,10 +16,11 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
public class Details : FillFlowContainer
|
public class Details : FillFlowContainer
|
||||||
{
|
{
|
||||||
|
protected readonly UserRatings Ratings;
|
||||||
|
|
||||||
private readonly PreviewButton preview;
|
private readonly PreviewButton preview;
|
||||||
private readonly BasicStats basic;
|
private readonly BasicStats basic;
|
||||||
private readonly AdvancedStats advanced;
|
private readonly AdvancedStats advanced;
|
||||||
private readonly UserRatings ratings;
|
|
||||||
|
|
||||||
private BeatmapSetInfo beatmapSet;
|
private BeatmapSetInfo beatmapSet;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
beatmapSet = value;
|
beatmapSet = value;
|
||||||
|
|
||||||
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
|
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
|
||||||
|
updateDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,13 +48,12 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
if (value == beatmap) return;
|
if (value == beatmap) return;
|
||||||
|
|
||||||
basic.Beatmap = advanced.Beatmap = beatmap = value;
|
basic.Beatmap = advanced.Beatmap = beatmap = value;
|
||||||
updateDisplay();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisplay()
|
private void updateDisplay()
|
||||||
{
|
{
|
||||||
ratings.Metrics = Beatmap?.Metrics;
|
Ratings.Metrics = BeatmapSet?.Metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Details()
|
public Details()
|
||||||
@ -87,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
},
|
},
|
||||||
new DetailBox
|
new DetailBox
|
||||||
{
|
{
|
||||||
Child = ratings = new UserRatings
|
Child = Ratings = new UserRatings
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 95,
|
Height = 95,
|
||||||
|
@ -14,11 +14,12 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
public class SuccessRate : Container
|
public class SuccessRate : Container
|
||||||
{
|
{
|
||||||
|
protected readonly FailRetryGraph Graph;
|
||||||
|
|
||||||
private readonly FillFlowContainer header;
|
private readonly FillFlowContainer header;
|
||||||
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
|
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
|
||||||
private readonly Bar successRate;
|
private readonly Bar successRate;
|
||||||
private readonly Container percentContainer;
|
private readonly Container percentContainer;
|
||||||
private readonly FailRetryGraph graph;
|
|
||||||
|
|
||||||
private BeatmapInfo beatmap;
|
private BeatmapInfo beatmap;
|
||||||
|
|
||||||
@ -37,15 +38,15 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
private void updateDisplay()
|
private void updateDisplay()
|
||||||
{
|
{
|
||||||
int passCount = beatmap?.OnlineInfo.PassCount ?? 0;
|
int passCount = beatmap?.OnlineInfo?.PassCount ?? 0;
|
||||||
int playCount = beatmap?.OnlineInfo.PlayCount ?? 0;
|
int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
|
||||||
|
|
||||||
var rate = playCount != 0 ? (float)passCount / playCount : 0;
|
var rate = playCount != 0 ? (float)passCount / playCount : 0;
|
||||||
successPercent.Text = rate.ToString("P0");
|
successPercent.Text = rate.ToString("P0");
|
||||||
successRate.Length = rate;
|
successRate.Length = rate;
|
||||||
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
|
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
|
||||||
|
|
||||||
graph.Metrics = beatmap?.Metrics;
|
Graph.Metrics = beatmap?.Metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SuccessRate()
|
public SuccessRate()
|
||||||
@ -94,7 +95,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
graph = new FailRetryGraph
|
Graph = new FailRetryGraph
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
@ -117,7 +118,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
Graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private RulesetStore rulesets;
|
private RulesetStore rulesets;
|
||||||
|
|
||||||
private readonly ScrollContainer scroll;
|
private readonly OsuScrollContainer scroll;
|
||||||
|
|
||||||
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
|
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ namespace osu.Game.Overlays
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(0.2f)
|
Colour = OsuColour.Gray(0.2f)
|
||||||
},
|
},
|
||||||
scroll = new ScrollContainer
|
scroll = new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ScrollbarVisible = false,
|
ScrollbarVisible = false,
|
||||||
@ -92,7 +92,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Overlays
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colour.PurpleDarkAlternative,
|
Colour = colour.PurpleDarkAlternative,
|
||||||
},
|
},
|
||||||
new ScrollContainer
|
new OsuScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ScrollbarVisible = false,
|
ScrollbarVisible = false,
|
||||||
@ -92,7 +92,7 @@ namespace osu.Game.Overlays
|
|||||||
public void ShowListing()
|
public void ShowListing()
|
||||||
{
|
{
|
||||||
Current.Value = null;
|
Current.Value = null;
|
||||||
State = Visibility.Visible;
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -106,7 +106,7 @@ namespace osu.Game.Overlays
|
|||||||
if (build == null) throw new ArgumentNullException(nameof(build));
|
if (build == null) throw new ArgumentNullException(nameof(build));
|
||||||
|
|
||||||
Current.Value = build;
|
Current.Value = build;
|
||||||
State = Visibility.Visible;
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowBuild([NotNull] string updateStream, [NotNull] string version)
|
public void ShowBuild([NotNull] string updateStream, [NotNull] string version)
|
||||||
@ -123,7 +123,7 @@ namespace osu.Game.Overlays
|
|||||||
ShowBuild(build);
|
ShowBuild(build);
|
||||||
});
|
});
|
||||||
|
|
||||||
State = Visibility.Visible;
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnPressed(GlobalAction action)
|
public override bool OnPressed(GlobalAction action)
|
||||||
@ -133,7 +133,7 @@ namespace osu.Game.Overlays
|
|||||||
case GlobalAction.Back:
|
case GlobalAction.Back:
|
||||||
if (Current.Value == null)
|
if (Current.Value == null)
|
||||||
{
|
{
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
{
|
{
|
||||||
public readonly Channel Channel;
|
public readonly Channel Channel;
|
||||||
protected readonly ChatLineContainer ChatLineFlow;
|
protected readonly ChatLineContainer ChatLineFlow;
|
||||||
private readonly ScrollContainer scroll;
|
private readonly OsuScrollContainer scroll;
|
||||||
|
|
||||||
public DrawableChannel(Channel channel)
|
public DrawableChannel(Channel channel)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Overlays
|
|||||||
private readonly Container channelSelectionContainer;
|
private readonly Container channelSelectionContainer;
|
||||||
private readonly ChannelSelectionOverlay channelSelectionOverlay;
|
private readonly ChannelSelectionOverlay channelSelectionOverlay;
|
||||||
|
|
||||||
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelectionOverlay.State == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos);
|
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || (channelSelectionOverlay.State.Value == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos));
|
||||||
|
|
||||||
public ChatOverlay()
|
public ChatOverlay()
|
||||||
{
|
{
|
||||||
@ -130,7 +130,7 @@ namespace osu.Game.Overlays
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 1,
|
Height = 1,
|
||||||
PlaceholderText = "type your message",
|
PlaceholderText = "type your message",
|
||||||
Exit = () => State = Visibility.Hidden,
|
Exit = Hide,
|
||||||
OnCommit = postMessage,
|
OnCommit = postMessage,
|
||||||
ReleaseFocusOnCommit = false,
|
ReleaseFocusOnCommit = false,
|
||||||
HoldFocus = true,
|
HoldFocus = true,
|
||||||
@ -163,19 +163,19 @@ namespace osu.Game.Overlays
|
|||||||
};
|
};
|
||||||
|
|
||||||
channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
|
channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
|
||||||
channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State = active.NewValue ? Visibility.Visible : Visibility.Hidden;
|
channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden;
|
||||||
channelSelectionOverlay.StateChanged += state =>
|
channelSelectionOverlay.State.ValueChanged += state =>
|
||||||
{
|
{
|
||||||
if (state == Visibility.Hidden && channelManager.CurrentChannel.Value == null)
|
if (state.NewValue == Visibility.Hidden && channelManager.CurrentChannel.Value == null)
|
||||||
{
|
{
|
||||||
channelSelectionOverlay.State = Visibility.Visible;
|
channelSelectionOverlay.Show();
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
channelTabControl.ChannelSelectorActive.Value = state == Visibility.Visible;
|
channelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible;
|
||||||
|
|
||||||
if (state == Visibility.Visible)
|
if (state.NewValue == Visibility.Visible)
|
||||||
{
|
{
|
||||||
textbox.HoldFocus = false;
|
textbox.HoldFocus = false;
|
||||||
if (1f - ChatHeight.Value < channel_selection_min_height)
|
if (1f - ChatHeight.Value < channel_selection_min_height)
|
||||||
@ -195,7 +195,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
textbox.Current.Disabled = true;
|
textbox.Current.Disabled = true;
|
||||||
currentChannelContainer.Clear(false);
|
currentChannelContainer.Clear(false);
|
||||||
channelSelectionOverlay.State = Visibility.Visible;
|
channelSelectionOverlay.Show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +253,7 @@ namespace osu.Game.Overlays
|
|||||||
double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
|
double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
|
||||||
|
|
||||||
// If the channel selection screen is shown, mind its minimum height
|
// If the channel selection screen is shown, mind its minimum height
|
||||||
if (channelSelectionOverlay.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
if (channelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
||||||
targetChatHeight = 1f - channel_selection_min_height;
|
targetChatHeight = 1f - channel_selection_min_height;
|
||||||
|
|
||||||
ChatHeight.Value = targetChatHeight;
|
ChatHeight.Value = targetChatHeight;
|
||||||
@ -325,7 +325,7 @@ namespace osu.Game.Overlays
|
|||||||
this.MoveToY(Height, transition_length, Easing.InSine);
|
this.MoveToY(Height, transition_length, Easing.InSine);
|
||||||
this.FadeOut(transition_length, Easing.InSine);
|
this.FadeOut(transition_length, Easing.InSine);
|
||||||
|
|
||||||
channelSelectionOverlay.State = Visibility.Hidden;
|
channelSelectionOverlay.Hide();
|
||||||
|
|
||||||
textbox.HoldFocus = false;
|
textbox.HoldFocus = false;
|
||||||
base.PopOut();
|
base.PopOut();
|
||||||
|
@ -37,8 +37,8 @@ namespace osu.Game.Overlays
|
|||||||
dialogContainer.Add(currentDialog);
|
dialogContainer.Add(currentDialog);
|
||||||
|
|
||||||
currentDialog.Show();
|
currentDialog.Show();
|
||||||
currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
|
currentDialog.State.ValueChanged += state => onDialogOnStateChanged(dialog, state.NewValue);
|
||||||
State = Visibility.Visible;
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool PlaySamplesOnStateChange => false;
|
protected override bool PlaySamplesOnStateChange => false;
|
||||||
@ -53,7 +53,7 @@ namespace osu.Game.Overlays
|
|||||||
dialog.Delay(PopupDialog.EXIT_DURATION).Expire();
|
dialog.Delay(PopupDialog.EXIT_DURATION).Expire();
|
||||||
|
|
||||||
if (dialog == currentDialog)
|
if (dialog == currentDialog)
|
||||||
State = Visibility.Hidden;
|
Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
@ -66,7 +66,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.PopOut();
|
base.PopOut();
|
||||||
|
|
||||||
if (currentDialog?.State == Visibility.Visible)
|
if (currentDialog?.State.Value == Visibility.Visible)
|
||||||
{
|
{
|
||||||
currentDialog.Hide();
|
currentDialog.Hide();
|
||||||
return;
|
return;
|
||||||
|
@ -252,7 +252,7 @@ namespace osu.Game.Overlays
|
|||||||
if (!IsLoaded)
|
if (!IsLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (State == Visibility.Hidden)
|
if (State.Value == Visibility.Hidden)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (API == null)
|
if (API == null)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -40,6 +41,19 @@ namespace osu.Game.Overlays
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
if (State.Value == Visibility.Visible)
|
||||||
|
{
|
||||||
|
// re-trigger the state changed so we can potentially surface to front
|
||||||
|
State.TriggerChange();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
base.PopIn();
|
base.PopIn();
|
||||||
|
@ -413,12 +413,12 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
if (selectedMod != null)
|
if (selectedMod != null)
|
||||||
{
|
{
|
||||||
if (State == Visibility.Visible) sampleOn?.Play();
|
if (State.Value == Visibility.Visible) sampleOn?.Play();
|
||||||
DeselectTypes(selectedMod.IncompatibleMods, true);
|
DeselectTypes(selectedMod.IncompatibleMods, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (State == Visibility.Visible) sampleOff?.Play();
|
if (State.Value == Visibility.Visible) sampleOff?.Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshSelectedMods();
|
refreshSelectedMods();
|
||||||
|
@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
ExitRequested = () => State = Visibility.Hidden,
|
ExitRequested = Hide,
|
||||||
FilterChanged = search => list.Filter(search),
|
FilterChanged = search => list.Filter(search),
|
||||||
Padding = new MarginPadding(10),
|
Padding = new MarginPadding(10),
|
||||||
},
|
},
|
||||||
|
@ -200,7 +200,7 @@ namespace osu.Game.Overlays
|
|||||||
beatmaps.ItemAdded += handleBeatmapAdded;
|
beatmaps.ItemAdded += handleBeatmapAdded;
|
||||||
beatmaps.ItemRemoved += handleBeatmapRemoved;
|
beatmaps.ItemRemoved += handleBeatmapRemoved;
|
||||||
|
|
||||||
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate seekDelegate;
|
private ScheduledDelegate seekDelegate;
|
||||||
@ -449,7 +449,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
// This is here mostly as a performance fix.
|
// This is here mostly as a performance fix.
|
||||||
// If the playlist is not hidden it will update children even when the music controller is hidden (due to AlwaysPresent).
|
// If the playlist is not hidden it will update children even when the music controller is hidden (due to AlwaysPresent).
|
||||||
playlist.State = Visibility.Hidden;
|
playlist.Hide();
|
||||||
|
|
||||||
this.FadeOut(transition_length, Easing.OutQuint);
|
this.FadeOut(transition_length, Easing.OutQuint);
|
||||||
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
|
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
|
||||||
@ -464,12 +464,26 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private class MusicIconButton : IconButton
|
private class MusicIconButton : IconButton
|
||||||
{
|
{
|
||||||
|
public MusicIconButton()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
HoverColour = colours.YellowDark.Opacity(0.6f);
|
HoverColour = colours.YellowDark.Opacity(0.6f);
|
||||||
FlashColour = colours.Yellow;
|
FlashColour = colours.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
// works with AutoSizeAxes above to make buttons autosize with the scale animation.
|
||||||
|
Content.AutoSizeAxes = Axes.None;
|
||||||
|
Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Background : BufferedContainer
|
private class Background : BufferedContainer
|
||||||
|
@ -81,13 +81,13 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private void updateProcessingMode()
|
private void updateProcessingMode()
|
||||||
{
|
{
|
||||||
bool enabled = OverlayActivationMode.Value == OverlayActivation.All || State == Visibility.Visible;
|
bool enabled = OverlayActivationMode.Value == OverlayActivation.All || State.Value == Visibility.Visible;
|
||||||
|
|
||||||
notificationsEnabler?.Cancel();
|
notificationsEnabler?.Cancel();
|
||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
// we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
|
// we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
|
||||||
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State == Visibility.Visible ? 0 : 1000);
|
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, State.Value == Visibility.Visible ? 0 : 1000);
|
||||||
else
|
else
|
||||||
processingPosts = false;
|
processingPosts = false;
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
StateChanged += _ => updateProcessingMode();
|
State.ValueChanged += _ => updateProcessingMode();
|
||||||
OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true);
|
OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ namespace osu.Game.Overlays
|
|||||||
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
|
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
|
||||||
|
|
||||||
if (notification.IsImportant)
|
if (notification.IsImportant)
|
||||||
State = Visibility.Visible;
|
Show();
|
||||||
|
|
||||||
updateCounts();
|
updateCounts();
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user