mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 03:13:21 +08:00
Merge branch 'master' into beginplaying-score-token
This commit is contained in:
commit
849245b90c
@ -6,9 +6,9 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public partial class CatchRelaxCursorContainer : GameplayCursorContainer
|
public partial class CatchCursorContainer : GameplayCursorContainer
|
||||||
{
|
{
|
||||||
// Just hide the cursor in relax.
|
// Just hide the cursor.
|
||||||
// The main goal here is to show that we have a cursor so the game never shows the global one.
|
// The main goal here is to show that we have a cursor so the game never shows the global one.
|
||||||
protected override Drawable CreateCursor() => Empty();
|
protected override Drawable CreateCursor() => Empty();
|
||||||
}
|
}
|
@ -3,14 +3,12 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -52,13 +50,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
this.difficulty = difficulty;
|
this.difficulty = difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override GameplayCursorContainer CreateCursor()
|
protected override GameplayCursorContainer CreateCursor() => new CatchCursorContainer();
|
||||||
{
|
|
||||||
if (Mods != null && Mods.Any(m => m is ModRelax))
|
|
||||||
return new CatchRelaxCursorContainer();
|
|
||||||
|
|
||||||
return base.CreateCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
|
@ -16,7 +16,9 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
using osu.Game.Rulesets.Mania;
|
using osu.Game.Rulesets.Mania;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
@ -179,6 +181,40 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSoloScoreData()
|
||||||
|
{
|
||||||
|
var ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
|
var scoreInfo = TestResources.CreateTestScoreInfo(ruleset);
|
||||||
|
scoreInfo.Mods = new Mod[]
|
||||||
|
{
|
||||||
|
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
|
||||||
|
};
|
||||||
|
|
||||||
|
var beatmap = new TestBeatmap(ruleset);
|
||||||
|
var score = new Score
|
||||||
|
{
|
||||||
|
ScoreInfo = scoreInfo,
|
||||||
|
Replay = new Replay
|
||||||
|
{
|
||||||
|
Frames = new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(decodedAfterEncode.ScoreInfo.Statistics, Is.EqualTo(scoreInfo.Statistics));
|
||||||
|
Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics));
|
||||||
|
Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap)
|
private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var encodeStream = new MemoryStream();
|
var encodeStream = new MemoryStream();
|
||||||
|
@ -117,11 +117,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
BeatmapID = 0,
|
BeatmapID = 0,
|
||||||
RulesetID = 0,
|
RulesetID = 0,
|
||||||
Mods = user.Mods,
|
Mods = user.Mods,
|
||||||
MaximumScoringValues = new ScoringValues
|
MaximumStatistics = new Dictionary<HitResult, int>
|
||||||
{
|
{
|
||||||
BaseScore = 10000,
|
{ HitResult.Perfect, 100 }
|
||||||
MaxCombo = 1000,
|
|
||||||
CountBasicHitObjects = 1000
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
129
osu.Game.Tests/Visual/UserInterface/TestSceneButtonsInput.cs
Normal file
129
osu.Game.Tests/Visual/UserInterface/TestSceneButtonsInput.cs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osuTK;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public partial class TestSceneButtonsInput : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private const int width = 500;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
|
|
||||||
|
private readonly SettingsButton settingsButton;
|
||||||
|
private readonly OsuClickableContainer clickableContainer;
|
||||||
|
private readonly RoundedButton roundedButton;
|
||||||
|
private readonly ShearedButton shearedButton;
|
||||||
|
|
||||||
|
public TestSceneButtonsInput()
|
||||||
|
{
|
||||||
|
Add(new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Width = 500,
|
||||||
|
Spacing = new Vector2(0, 5),
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
clickableContainer = new OsuClickableContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 40,
|
||||||
|
Enabled = { Value = true },
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 20,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Red
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = "Rounded clickable container"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
settingsButton = new SettingsButton
|
||||||
|
{
|
||||||
|
Enabled = { Value = true },
|
||||||
|
Text = "Settings button"
|
||||||
|
},
|
||||||
|
roundedButton = new RoundedButton
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Enabled = { Value = true },
|
||||||
|
Text = "Rounded button"
|
||||||
|
},
|
||||||
|
shearedButton = new ShearedButton(width)
|
||||||
|
{
|
||||||
|
Text = "Sheared button",
|
||||||
|
LighterColour = Colour4.FromHex("#FFFFFF"),
|
||||||
|
DarkerColour = Colour4.FromHex("#FFCC22"),
|
||||||
|
TextColour = Colour4.Black,
|
||||||
|
Height = 40,
|
||||||
|
Enabled = { Value = true },
|
||||||
|
Padding = new MarginPadding(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSettingsButtonInput()
|
||||||
|
{
|
||||||
|
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(settingsButton));
|
||||||
|
AddAssert("Button is hovered", () => settingsButton.IsHovered);
|
||||||
|
AddStep("Move cursor to padded area", () => InputManager.MoveMouseTo(settingsButton.ScreenSpaceDrawQuad.TopLeft + new Vector2(SettingsPanel.CONTENT_MARGINS / 2f, 10)));
|
||||||
|
AddAssert("Cursor within a button", () => settingsButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
|
||||||
|
AddAssert("Button is not hovered", () => !settingsButton.IsHovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRoundedButtonInput()
|
||||||
|
{
|
||||||
|
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(roundedButton));
|
||||||
|
AddAssert("Button is hovered", () => roundedButton.IsHovered);
|
||||||
|
AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(roundedButton.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
|
||||||
|
AddAssert("Cursor within a button", () => roundedButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
|
||||||
|
AddAssert("Button is not hovered", () => !roundedButton.IsHovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShearedButtonInput()
|
||||||
|
{
|
||||||
|
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(shearedButton));
|
||||||
|
AddAssert("Button is hovered", () => shearedButton.IsHovered);
|
||||||
|
AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(shearedButton.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
|
||||||
|
AddAssert("Cursor within a button", () => shearedButton.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
|
||||||
|
AddAssert("Button is not hovered", () => !shearedButton.IsHovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRoundedClickableContainerInput()
|
||||||
|
{
|
||||||
|
AddStep("Move cursor to button", () => InputManager.MoveMouseTo(clickableContainer));
|
||||||
|
AddAssert("Button is hovered", () => clickableContainer.IsHovered);
|
||||||
|
AddStep("Move cursor to corner", () => InputManager.MoveMouseTo(clickableContainer.ScreenSpaceDrawQuad.TopLeft + Vector2.One));
|
||||||
|
AddAssert("Cursor within a button", () => clickableContainer.ScreenSpaceDrawQuad.Contains(InputManager.CurrentState.Mouse.Position));
|
||||||
|
AddAssert("Button is not hovered", () => !clickableContainer.IsHovered);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +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.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
|
||||||
{
|
|
||||||
public partial class TestSceneOsuButton : OsuTestScene
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void TestToggleEnabled()
|
|
||||||
{
|
|
||||||
OsuButton button = null;
|
|
||||||
|
|
||||||
AddStep("add button", () => Child = button = new OsuButton
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Size = new Vector2(200),
|
|
||||||
Text = "Button"
|
|
||||||
});
|
|
||||||
|
|
||||||
AddToggleStep("toggle enabled", toggle =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 6; i++)
|
|
||||||
button.Action = toggle ? () => { } : null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestInitiallyDisabled()
|
|
||||||
{
|
|
||||||
AddStep("add button", () => Child = new OsuButton
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Size = new Vector2(200),
|
|
||||||
Text = "Button"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,13 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
{
|
{
|
||||||
@ -18,6 +17,12 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
private readonly Container content = new Container { RelativeSizeAxes = Axes.Both };
|
private readonly Container content = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||||
|
// base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation).
|
||||||
|
base.ReceivePositionalInputAt(screenSpacePos)
|
||||||
|
// Implementations often apply masking / edge rounding at a content level, so it's imperative to check that as well.
|
||||||
|
&& Content.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
protected virtual HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet) { Enabled = { BindTarget = Enabled } };
|
protected virtual HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds(sampleSet) { Enabled = { BindTarget = Enabled } };
|
||||||
@ -38,11 +43,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
content.AutoSizeAxes = AutoSizeAxes;
|
content.AutoSizeAxes = AutoSizeAxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
AddInternal(content);
|
||||||
{
|
Add(CreateHoverSounds(sampleSet));
|
||||||
content,
|
|
||||||
CreateHoverSounds(sampleSet)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
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;
|
||||||
@ -13,6 +11,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
@ -20,16 +19,12 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A button with added default sound effects.
|
/// A button with added default sound effects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class OsuButton : Button
|
public abstract partial class OsuButton : Button
|
||||||
{
|
{
|
||||||
public LocalisableString Text
|
public LocalisableString Text
|
||||||
{
|
{
|
||||||
get => SpriteText?.Text ?? default;
|
get => SpriteText.Text;
|
||||||
set
|
set => SpriteText.Text = value;
|
||||||
{
|
|
||||||
if (SpriteText != null)
|
|
||||||
SpriteText.Text = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4? backgroundColour;
|
private Color4? backgroundColour;
|
||||||
@ -66,13 +61,19 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected override Container<Drawable> Content { get; }
|
protected override Container<Drawable> Content { get; }
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||||
|
// base call is checked for cases when `OsuClickableContainer` has masking applied to it directly (ie. externally in object initialisation).
|
||||||
|
base.ReceivePositionalInputAt(screenSpacePos)
|
||||||
|
// Implementations often apply masking / edge rounding at a content level, so it's imperative to check that as well.
|
||||||
|
&& Content.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
protected Box Hover;
|
protected Box Hover;
|
||||||
protected Box Background;
|
protected Box Background;
|
||||||
protected SpriteText SpriteText;
|
protected SpriteText SpriteText;
|
||||||
|
|
||||||
private readonly Box flashLayer;
|
private readonly Box flashLayer;
|
||||||
|
|
||||||
public OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
|
protected OsuButton(HoverSampleSet? hoverSounds = HoverSampleSet.Button)
|
||||||
{
|
{
|
||||||
Height = 40;
|
Height = 40;
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (hoverSounds.HasValue)
|
if (hoverSounds.HasValue)
|
||||||
AddInternal(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
|
Add(new HoverClickSounds(hoverSounds.Value) { Enabled = { BindTarget = Enabled } });
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -175,7 +175,7 @@ namespace osu.Game.Online.Spectator
|
|||||||
currentState.RulesetID = score.ScoreInfo.RulesetID;
|
currentState.RulesetID = score.ScoreInfo.RulesetID;
|
||||||
currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray();
|
currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray();
|
||||||
currentState.State = SpectatedUserState.Playing;
|
currentState.State = SpectatedUserState.Playing;
|
||||||
currentState.MaximumScoringValues = state.ScoreProcessor.MaximumScoringValues;
|
currentState.MaximumStatistics = state.ScoreProcessor.MaximumStatistics;
|
||||||
|
|
||||||
currentBeatmap = state.Beatmap;
|
currentBeatmap = state.Beatmap;
|
||||||
currentScore = score;
|
currentScore = score;
|
||||||
|
@ -152,12 +152,12 @@ namespace osu.Game.Online.Spectator
|
|||||||
|
|
||||||
scoreInfo.MaxCombo = frame.Header.MaxCombo;
|
scoreInfo.MaxCombo = frame.Header.MaxCombo;
|
||||||
scoreInfo.Statistics = frame.Header.Statistics;
|
scoreInfo.Statistics = frame.Header.Statistics;
|
||||||
|
scoreInfo.MaximumStatistics = spectatorState.MaximumStatistics;
|
||||||
|
|
||||||
Accuracy.Value = frame.Header.Accuracy;
|
Accuracy.Value = frame.Header.Accuracy;
|
||||||
Combo.Value = frame.Header.Combo;
|
Combo.Value = frame.Header.Combo;
|
||||||
|
|
||||||
scoreProcessor.ExtractScoringValues(frame.Header, out var currentScoringValues, out _);
|
TotalScore.Value = scoreProcessor.ComputeScore(Mode.Value, scoreInfo);
|
||||||
TotalScore.Value = scoreProcessor.ComputeScore(Mode.Value, currentScoringValues, spectatorState.MaximumScoringValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -9,7 +9,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Online.Spectator
|
namespace osu.Game.Online.Spectator
|
||||||
{
|
{
|
||||||
@ -31,7 +31,7 @@ namespace osu.Game.Online.Spectator
|
|||||||
public SpectatedUserState State { get; set; }
|
public SpectatedUserState State { get; set; }
|
||||||
|
|
||||||
[Key(4)]
|
[Key(4)]
|
||||||
public ScoringValues MaximumScoringValues { get; set; }
|
public Dictionary<HitResult, int> MaximumStatistics { get; set; } = new Dictionary<HitResult, int>();
|
||||||
|
|
||||||
public bool Equals(SpectatorState other)
|
public bool Equals(SpectatorState other)
|
||||||
{
|
{
|
||||||
|
@ -68,11 +68,15 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Vertical,
|
||||||
Margin = new MarginPadding { Top = 20 },
|
Margin = new MarginPadding { Top = 20 },
|
||||||
Children = new Drawable[]
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
new OsuHoverContainer
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Child = new OsuHoverContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -104,27 +103,29 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
{
|
{
|
||||||
var fill = base.CreateHeader();
|
var fill = base.CreateHeader();
|
||||||
|
|
||||||
foreach (var existing in fill.Children.OfType<OsuHoverContainer>())
|
var nestedFill = (FillFlowContainer)fill.Child;
|
||||||
{
|
|
||||||
existing.Scale = new Vector2(1.25f);
|
|
||||||
existing.Action = null;
|
|
||||||
|
|
||||||
existing.Add(date = new OsuSpriteText
|
var buildDisplay = (OsuHoverContainer)nestedFill.Child;
|
||||||
|
|
||||||
|
buildDisplay.Scale = new Vector2(1.25f);
|
||||||
|
buildDisplay.Action = null;
|
||||||
|
|
||||||
|
fill.Add(date = new OsuSpriteText
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
Text = Build.CreatedAt.Date.ToString("dd MMMM yyyy"),
|
Text = Build.CreatedAt.Date.ToString("dd MMMM yyyy"),
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 14),
|
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 14),
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Margin = new MarginPadding { Top = 5 },
|
Margin = new MarginPadding { Top = 5 },
|
||||||
|
Scale = new Vector2(1.25f),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
fill.Insert(-1, new NavigationIconButton(Build.Versions?.Previous)
|
nestedFill.Insert(-1, new NavigationIconButton(Build.Versions?.Previous)
|
||||||
{
|
{
|
||||||
Icon = FontAwesome.Solid.ChevronLeft,
|
Icon = FontAwesome.Solid.ChevronLeft,
|
||||||
SelectBuild = b => SelectBuild(b)
|
SelectBuild = b => SelectBuild(b)
|
||||||
});
|
});
|
||||||
fill.Insert(1, new NavigationIconButton(Build.Versions?.Next)
|
nestedFill.Insert(1, new NavigationIconButton(Build.Versions?.Next)
|
||||||
{
|
{
|
||||||
Icon = FontAwesome.Solid.ChevronRight,
|
Icon = FontAwesome.Solid.ChevronRight,
|
||||||
SelectBuild = b => SelectBuild(b)
|
SelectBuild = b => SelectBuild(b)
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
public Color4 AccentColour { get; }
|
public Color4 AccentColour { get; }
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||||
Child.ReceivePositionalInputAt(screenSpacePos);
|
colouredDrawable.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
public float FontSize
|
public float FontSize
|
||||||
{
|
{
|
||||||
@ -87,13 +87,13 @@ namespace osu.Game.Overlays.Chat
|
|||||||
{
|
{
|
||||||
AccentColour = default_colours[user.Id % default_colours.Length];
|
AccentColour = default_colours[user.Id % default_colours.Length];
|
||||||
|
|
||||||
Child = colouredDrawable = drawableText;
|
Add(colouredDrawable = drawableText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AccentColour = Color4Extensions.FromHex(user.Colour);
|
AccentColour = Color4Extensions.FromHex(user.Colour);
|
||||||
|
|
||||||
Child = new Container
|
Add(new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ namespace osu.Game.Rulesets.Configuration
|
|||||||
{
|
{
|
||||||
var setting = r.All<RealmRulesetSetting>().First(s => s.RulesetName == rulesetName && s.Variant == variant && s.Key == c.ToString());
|
var setting = r.All<RealmRulesetSetting>().First(s => s.RulesetName == rulesetName && s.Variant == variant && s.Key == c.ToString());
|
||||||
|
|
||||||
setting.Value = ConfigStore[c].ToString();
|
setting.Value = ConfigStore[c].ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,7 +91,7 @@ namespace osu.Game.Rulesets.Configuration
|
|||||||
setting = new RealmRulesetSetting
|
setting = new RealmRulesetSetting
|
||||||
{
|
{
|
||||||
Key = lookup.ToString(),
|
Key = lookup.ToString(),
|
||||||
Value = bindable.Value.ToString(),
|
Value = bindable.ToString(CultureInfo.InvariantCulture),
|
||||||
RulesetName = rulesetName,
|
RulesetName = rulesetName,
|
||||||
Variant = variant,
|
Variant = variant,
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.Spectator;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -90,17 +89,14 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
private readonly double accuracyPortion;
|
private readonly double accuracyPortion;
|
||||||
private readonly double comboPortion;
|
private readonly double comboPortion;
|
||||||
|
|
||||||
/// <summary>
|
public Dictionary<HitResult, int> MaximumStatistics
|
||||||
/// Scoring values for a perfect play.
|
|
||||||
/// </summary>
|
|
||||||
public ScoringValues MaximumScoringValues
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!beatmapApplied)
|
if (!beatmapApplied)
|
||||||
throw new InvalidOperationException($"Cannot access maximum scoring values before calling {nameof(ApplyBeatmap)}.");
|
throw new InvalidOperationException($"Cannot access maximum statistics before calling {nameof(ApplyBeatmap)}.");
|
||||||
|
|
||||||
return maximumScoringValues;
|
return new Dictionary<HitResult, int>(maximumResultCounts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +264,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
private void updateScore()
|
private void updateScore()
|
||||||
{
|
{
|
||||||
Accuracy.Value = currentMaximumScoringValues.BaseScore > 0 ? (double)currentScoringValues.BaseScore / currentMaximumScoringValues.BaseScore : 1;
|
Accuracy.Value = currentMaximumScoringValues.BaseScore > 0 ? (double)currentScoringValues.BaseScore / currentMaximumScoringValues.BaseScore : 1;
|
||||||
TotalScore.Value = ComputeScore(Mode.Value, currentScoringValues, maximumScoringValues);
|
TotalScore.Value = computeScore(Mode.Value, currentScoringValues, maximumScoringValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -303,9 +299,9 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||||
|
|
||||||
ExtractScoringValues(scoreInfo, out var current, out var maximum);
|
extractScoringValues(scoreInfo, out var current, out var maximum);
|
||||||
|
|
||||||
return ComputeScore(mode, current, maximum);
|
return computeScore(mode, current, maximum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -316,7 +312,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// <param name="maximum">The maximum scoring values.</param>
|
/// <param name="maximum">The maximum scoring values.</param>
|
||||||
/// <returns>The total score computed from the given scoring values.</returns>
|
/// <returns>The total score computed from the given scoring values.</returns>
|
||||||
[Pure]
|
[Pure]
|
||||||
public long ComputeScore(ScoringMode mode, ScoringValues current, ScoringValues maximum)
|
private long computeScore(ScoringMode mode, ScoringValues current, ScoringValues maximum)
|
||||||
{
|
{
|
||||||
double accuracyRatio = maximum.BaseScore > 0 ? (double)current.BaseScore / maximum.BaseScore : 1;
|
double accuracyRatio = maximum.BaseScore > 0 ? (double)current.BaseScore / maximum.BaseScore : 1;
|
||||||
double comboRatio = maximum.MaxCombo > 0 ? (double)current.MaxCombo / maximum.MaxCombo : 1;
|
double comboRatio = maximum.MaxCombo > 0 ? (double)current.MaxCombo / maximum.MaxCombo : 1;
|
||||||
@ -474,14 +470,14 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// Consumers are expected to more accurately fill in the above values through external means.
|
/// Consumers are expected to more accurately fill in the above values through external means.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// <b>Ensure</b> to fill in the maximum <see cref="ScoringValues.CountBasicHitObjects"/> for use in
|
/// <b>Ensure</b> to fill in the maximum <see cref="ScoringValues.CountBasicHitObjects"/> for use in
|
||||||
/// <see cref="ComputeScore(osu.Game.Rulesets.Scoring.ScoringMode,osu.Game.Scoring.ScoringValues,osu.Game.Scoring.ScoringValues)"/>.
|
/// <see cref="computeScore(osu.Game.Rulesets.Scoring.ScoringMode,ScoringValues,ScoringValues)"/>.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="scoreInfo">The score to extract scoring values from.</param>
|
/// <param name="scoreInfo">The score to extract scoring values from.</param>
|
||||||
/// <param name="current">The "current" scoring values, representing the hit statistics as they appear.</param>
|
/// <param name="current">The "current" scoring values, representing the hit statistics as they appear.</param>
|
||||||
/// <param name="maximum">The "maximum" scoring values, representing the hit statistics as if the maximum hit result was attained each time.</param>
|
/// <param name="maximum">The "maximum" scoring values, representing the hit statistics as if the maximum hit result was attained each time.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
internal void ExtractScoringValues(ScoreInfo scoreInfo, out ScoringValues current, out ScoringValues maximum)
|
private void extractScoringValues(ScoreInfo scoreInfo, out ScoringValues current, out ScoringValues maximum)
|
||||||
{
|
{
|
||||||
extractScoringValues(scoreInfo.Statistics, out current, out maximum);
|
extractScoringValues(scoreInfo.Statistics, out current, out maximum);
|
||||||
current.MaxCombo = scoreInfo.MaxCombo;
|
current.MaxCombo = scoreInfo.MaxCombo;
|
||||||
@ -490,31 +486,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
extractScoringValues(scoreInfo.MaximumStatistics, out _, out maximum);
|
extractScoringValues(scoreInfo.MaximumStatistics, out _, out maximum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a best-effort extraction of hit statistics into <see cref="ScoringValues"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method is useful in a variety of situations, with a few drawbacks that need to be considered:
|
|
||||||
/// <list type="bullet">
|
|
||||||
/// <item>The maximum <see cref="ScoringValues.BonusScore"/> will always be 0.</item>
|
|
||||||
/// <item>The current and maximum <see cref="ScoringValues.CountBasicHitObjects"/> will always be the same value.</item>
|
|
||||||
/// </list>
|
|
||||||
/// Consumers are expected to more accurately fill in the above values through external means.
|
|
||||||
/// <para>
|
|
||||||
/// <b>Ensure</b> to fill in the maximum <see cref="ScoringValues.CountBasicHitObjects"/> for use in
|
|
||||||
/// <see cref="ComputeScore(osu.Game.Rulesets.Scoring.ScoringMode,osu.Game.Scoring.ScoringValues,osu.Game.Scoring.ScoringValues)"/>.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="header">The replay frame header to extract scoring values from.</param>
|
|
||||||
/// <param name="current">The "current" scoring values, representing the hit statistics as they appear.</param>
|
|
||||||
/// <param name="maximum">The "maximum" scoring values, representing the hit statistics as if the maximum hit result was attained each time.</param>
|
|
||||||
[Pure]
|
|
||||||
internal void ExtractScoringValues(FrameHeader header, out ScoringValues current, out ScoringValues maximum)
|
|
||||||
{
|
|
||||||
extractScoringValues(header.Statistics, out current, out maximum);
|
|
||||||
current.MaxCombo = header.MaxCombo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies a best-effort extraction of hit statistics into <see cref="ScoringValues"/>.
|
/// Applies a best-effort extraction of hit statistics into <see cref="ScoringValues"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -589,6 +560,32 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
hitEvents.Clear();
|
hitEvents.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the required scoring data that fulfils the minimum requirements for a <see cref="ScoreProcessor"/> to calculate score.
|
||||||
|
/// </summary>
|
||||||
|
private struct ScoringValues
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The sum of all "basic" <see cref="HitObject"/> scoring values. See: <see cref="HitResultExtensions.IsBasic"/> and <see cref="Judgement.ToNumericResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public long BaseScore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sum of all "bonus" <see cref="HitObject"/> scoring values. See: <see cref="HitResultExtensions.IsBonus"/> and <see cref="Judgement.ToNumericResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public long BonusScore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The highest achieved combo.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxCombo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The count of "basic" <see cref="HitObject"/>s. See: <see cref="HitResultExtensions.IsBasic"/>.
|
||||||
|
/// </summary>
|
||||||
|
public int CountBasicHitObjects;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScoringMode
|
public enum ScoringMode
|
||||||
|
38
osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs
Normal file
38
osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Scoring.Legacy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A minified version of <see cref="SoloScoreInfo"/> retrofit onto the end of legacy replay files (.osr),
|
||||||
|
/// containing the minimum data required to support storage of non-legacy replays.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
[JsonObject(MemberSerialization.OptIn)]
|
||||||
|
public class LegacyReplaySoloScoreInfo
|
||||||
|
{
|
||||||
|
[JsonProperty("mods")]
|
||||||
|
public APIMod[] Mods { get; set; } = Array.Empty<APIMod>();
|
||||||
|
|
||||||
|
[JsonProperty("statistics")]
|
||||||
|
public Dictionary<HitResult, int> Statistics { get; set; } = new Dictionary<HitResult, int>();
|
||||||
|
|
||||||
|
[JsonProperty("maximum_statistics")]
|
||||||
|
public Dictionary<HitResult, int> MaximumStatistics { get; set; } = new Dictionary<HitResult, int>();
|
||||||
|
|
||||||
|
public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo
|
||||||
|
{
|
||||||
|
Mods = score.APIMods,
|
||||||
|
Statistics = score.Statistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||||
|
MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,10 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
@ -91,9 +93,41 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
else if (version >= 20121008)
|
else if (version >= 20121008)
|
||||||
scoreInfo.OnlineID = sr.ReadInt32();
|
scoreInfo.OnlineID = sr.ReadInt32();
|
||||||
|
|
||||||
|
byte[] compressedScoreInfo = null;
|
||||||
|
|
||||||
|
if (version >= 30000001)
|
||||||
|
compressedScoreInfo = sr.ReadByteArray();
|
||||||
|
|
||||||
if (compressedReplay?.Length > 0)
|
if (compressedReplay?.Length > 0)
|
||||||
|
readCompressedData(compressedReplay, reader => readLegacyReplay(score.Replay, reader));
|
||||||
|
|
||||||
|
if (compressedScoreInfo?.Length > 0)
|
||||||
{
|
{
|
||||||
using (var replayInStream = new MemoryStream(compressedReplay))
|
readCompressedData(compressedScoreInfo, reader =>
|
||||||
|
{
|
||||||
|
LegacyReplaySoloScoreInfo readScore = JsonConvert.DeserializeObject<LegacyReplaySoloScoreInfo>(reader.ReadToEnd());
|
||||||
|
|
||||||
|
Debug.Assert(readScore != null);
|
||||||
|
|
||||||
|
score.ScoreInfo.Statistics = readScore.Statistics;
|
||||||
|
score.ScoreInfo.MaximumStatistics = readScore.MaximumStatistics;
|
||||||
|
score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PopulateAccuracy(score.ScoreInfo);
|
||||||
|
|
||||||
|
// before returning for database import, we must restore the database-sourced BeatmapInfo.
|
||||||
|
// if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception.
|
||||||
|
score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo;
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readCompressedData(byte[] data, Action<StreamReader> readFunc)
|
||||||
|
{
|
||||||
|
using (var replayInStream = new MemoryStream(data))
|
||||||
{
|
{
|
||||||
byte[] properties = new byte[5];
|
byte[] properties = new byte[5];
|
||||||
if (replayInStream.Read(properties, 0, 5) != 5)
|
if (replayInStream.Read(properties, 0, 5) != 5)
|
||||||
@ -114,19 +148,9 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
|
|
||||||
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
|
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
|
||||||
using (var reader = new StreamReader(lzma))
|
using (var reader = new StreamReader(lzma))
|
||||||
readLegacyReplay(score.Replay, reader);
|
readFunc(reader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
PopulateAccuracy(score.ScoreInfo);
|
|
||||||
|
|
||||||
// before returning for database import, we must restore the database-sourced BeatmapInfo.
|
|
||||||
// if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception.
|
|
||||||
score.ScoreInfo.BeatmapInfo = workingBeatmap.BeatmapInfo;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Populates the accuracy of a given <see cref="ScoreInfo"/> from its contained statistics.
|
/// Populates the accuracy of a given <see cref="ScoreInfo"/> from its contained statistics.
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.IO.Legacy;
|
using osu.Game.IO.Legacy;
|
||||||
|
using osu.Game.IO.Serialization;
|
||||||
using osu.Game.Replays.Legacy;
|
using osu.Game.Replays.Legacy;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
@ -24,7 +25,7 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
/// Database version in stable-compatible YYYYMMDD format.
|
/// Database version in stable-compatible YYYYMMDD format.
|
||||||
/// Should be incremented if any changes are made to the format/usage.
|
/// Should be incremented if any changes are made to the format/usage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int LATEST_VERSION = FIRST_LAZER_VERSION;
|
public const int LATEST_VERSION = 30000001;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
||||||
@ -52,9 +53,9 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
throw new ArgumentException(@"Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score));
|
throw new ArgumentException(@"Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Encode(Stream stream)
|
public void Encode(Stream stream, bool leaveOpen = false)
|
||||||
{
|
{
|
||||||
using (SerializationWriter sw = new SerializationWriter(stream))
|
using (SerializationWriter sw = new SerializationWriter(stream, leaveOpen))
|
||||||
{
|
{
|
||||||
sw.Write((byte)(score.ScoreInfo.Ruleset.OnlineID));
|
sw.Write((byte)(score.ScoreInfo.Ruleset.OnlineID));
|
||||||
sw.Write(LATEST_VERSION);
|
sw.Write(LATEST_VERSION);
|
||||||
@ -77,6 +78,7 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
sw.WriteByteArray(createReplayData());
|
sw.WriteByteArray(createReplayData());
|
||||||
sw.Write((long)0);
|
sw.Write((long)0);
|
||||||
writeModSpecificData(score.ScoreInfo, sw);
|
writeModSpecificData(score.ScoreInfo, sw);
|
||||||
|
sw.WriteByteArray(createScoreInfoData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,9 +86,13 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] createReplayData()
|
private byte[] createReplayData() => compress(replayStringContent);
|
||||||
|
|
||||||
|
private byte[] createScoreInfoData() => compress(LegacyReplaySoloScoreInfo.FromScore(score.ScoreInfo).Serialize());
|
||||||
|
|
||||||
|
private byte[] compress(string data)
|
||||||
{
|
{
|
||||||
byte[] content = new ASCIIEncoding().GetBytes(replayStringContent);
|
byte[] content = new ASCIIEncoding().GetBytes(data);
|
||||||
|
|
||||||
using (var outStream = new MemoryStream())
|
using (var outStream = new MemoryStream())
|
||||||
{
|
{
|
||||||
|
@ -1,43 +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.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using MessagePack;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Scoring
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the required scoring data that fulfils the minimum requirements for a <see cref="ScoreProcessor"/> to calculate score.
|
|
||||||
/// </summary>
|
|
||||||
[MessagePackObject]
|
|
||||||
public struct ScoringValues
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The sum of all "basic" <see cref="HitObject"/> scoring values. See: <see cref="HitResultExtensions.IsBasic"/> and <see cref="Judgement.ToNumericResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Key(0)]
|
|
||||||
public long BaseScore;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The sum of all "bonus" <see cref="HitObject"/> scoring values. See: <see cref="HitResultExtensions.IsBonus"/> and <see cref="Judgement.ToNumericResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Key(1)]
|
|
||||||
public long BonusScore;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The highest achieved combo.
|
|
||||||
/// </summary>
|
|
||||||
[Key(2)]
|
|
||||||
public int MaxCombo;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The count of "basic" <see cref="HitObject"/>s. See: <see cref="HitResultExtensions.IsBasic"/>.
|
|
||||||
/// </summary>
|
|
||||||
[Key(3)]
|
|
||||||
public int CountBasicHitObjects;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user