1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-14 19:22:56 +08:00

Merge pull request #9957 from Craftplacer/combo-colors

Implement combo colour serializing
This commit is contained in:
Dan Balasescu 2020-09-07 14:25:35 +09:00 committed by GitHub
commit 3347f57e46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 22 deletions

View File

@ -10,6 +10,7 @@ using System.Text;
using NUnit.Framework;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Formats;
@ -19,6 +20,7 @@ using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Skinning;
using osu.Game.Tests.Resources;
namespace osu.Game.Tests.Beatmaps.Formats
@ -26,18 +28,33 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture]
public class LegacyBeatmapEncoderTest
{
private static IEnumerable<string> allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu"));
private static readonly DllResourceStore beatmaps_resource_store = TestResources.GetStore();
private static IEnumerable<string> allBeatmaps = beatmaps_resource_store.GetAvailableResources().Where(res => res.EndsWith(".osu"));
[TestCaseSource(nameof(allBeatmaps))]
public void TestEncodeDecodeStability(string name)
{
var decoded = decodeFromLegacy(TestResources.GetStore().GetStream(name));
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded));
var decoded = decodeFromLegacy(beatmaps_resource_store.GetStream(name), name);
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy(decoded), name);
sort(decoded);
sort(decodedAfterEncode);
sort(decoded.beatmap);
sort(decodedAfterEncode.beatmap);
Assert.That(decodedAfterEncode.Serialize(), Is.EqualTo(decoded.Serialize()));
Assert.That(decodedAfterEncode.beatmap.Serialize(), Is.EqualTo(decoded.beatmap.Serialize()));
Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
}
private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b)
{
// equal to null, no need to SequenceEqual
if (a.ComboColours == null && b.ComboColours == null)
return true;
if (a.ComboColours == null || b.ComboColours == null)
return false;
return a.ComboColours.SequenceEqual(b.ComboColours);
}
private void sort(IBeatmap beatmap)
@ -50,18 +67,31 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
private IBeatmap decodeFromLegacy(Stream stream)
private (IBeatmap beatmap, TestLegacySkin beatmapSkin) decodeFromLegacy(Stream stream, string name)
{
using (var reader = new LineBufferedReader(stream))
return convert(new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(reader));
{
var beatmap = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(reader);
var beatmapSkin = new TestLegacySkin(beatmaps_resource_store, name);
return (convert(beatmap), beatmapSkin);
}
}
private Stream encodeToLegacy(IBeatmap beatmap)
private class TestLegacySkin : LegacySkin
{
public TestLegacySkin(IResourceStore<byte[]> storage, string fileName)
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, storage, null, fileName)
{
}
}
private Stream encodeToLegacy((IBeatmap beatmap, ISkin beatmapSkin) fullBeatmap)
{
var (beatmap, beatmapSkin) = fullBeatmap;
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
new LegacyBeatmapEncoder(beatmap).Encode(writer);
new LegacyBeatmapEncoder(beatmap, beatmapSkin).Encode(writer);
stream.Position = 0;

View File

@ -351,7 +351,7 @@ namespace osu.Game.Tests.Editing
using (var encoded = new MemoryStream())
{
using (var sw = new StreamWriter(encoded))
new LegacyBeatmapEncoder(beatmap).Encode(sw);
new LegacyBeatmapEncoder(beatmap, null).Encode(sw);
return encoded.ToArray();
}

View File

@ -27,6 +27,7 @@ using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Skinning;
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
namespace osu.Game.Beatmaps
@ -198,14 +199,15 @@ namespace osu.Game.Beatmaps
/// </summary>
/// <param name="info">The <see cref="BeatmapInfo"/> to save the content against. The file referenced by <see cref="BeatmapInfo.Path"/> will be replaced.</param>
/// <param name="beatmapContent">The <see cref="IBeatmap"/> content to write.</param>
public void Save(BeatmapInfo info, IBeatmap beatmapContent)
/// <param name="beatmapSkin">The beatmap <see cref="ISkin"/> content to write, null if to be omitted.</param>
public void Save(BeatmapInfo info, IBeatmap beatmapContent, ISkin beatmapSkin = null)
{
var setInfo = QueryBeatmapSet(s => s.Beatmaps.Any(b => b.ID == info.ID));
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
new LegacyBeatmapEncoder(beatmapContent).Encode(sw);
new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw);
stream.Seek(0, SeekOrigin.Begin);

View File

@ -8,6 +8,6 @@ namespace osu.Game.Beatmaps.Formats
{
public interface IHasCustomColours
{
Dictionary<string, Color4> CustomColours { get; set; }
Dictionary<string, Color4> CustomColours { get; }
}
}

View File

@ -7,13 +7,16 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
@ -23,9 +26,18 @@ namespace osu.Game.Beatmaps.Formats
private readonly IBeatmap beatmap;
public LegacyBeatmapEncoder(IBeatmap beatmap)
[CanBeNull]
private readonly ISkin skin;
/// <summary>
/// Creates a new <see cref="LegacyBeatmapEncoder"/>.
/// </summary>
/// <param name="beatmap">The beatmap to encode.</param>
/// <param name="skin">The beatmap's skin, used for encoding combo colours.</param>
public LegacyBeatmapEncoder(IBeatmap beatmap, [CanBeNull] ISkin skin)
{
this.beatmap = beatmap;
this.skin = skin;
if (beatmap.BeatmapInfo.RulesetID < 0 || beatmap.BeatmapInfo.RulesetID > 3)
throw new ArgumentException("Only beatmaps in the osu, taiko, catch, or mania rulesets can be encoded to the legacy beatmap format.", nameof(beatmap));
@ -53,6 +65,9 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine();
handleControlPoints(writer);
writer.WriteLine();
handleColours(writer);
writer.WriteLine();
handleHitObjects(writer);
}
@ -196,6 +211,28 @@ namespace osu.Game.Beatmaps.Formats
}
}
private void handleColours(TextWriter writer)
{
var colours = skin?.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value;
if (colours == null || colours.Count == 0)
return;
writer.WriteLine("[Colours]");
for (var i = 0; i < colours.Count; i++)
{
var comboColour = colours[i];
writer.Write(FormattableString.Invariant($"Combo{i}: "));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.R * byte.MaxValue)},"));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.G * byte.MaxValue)},"));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.B * byte.MaxValue)},"));
writer.Write(FormattableString.Invariant($"{(byte)(comboColour.A * byte.MaxValue)}"));
writer.WriteLine();
}
}
private void handleHitObjects(TextWriter writer)
{
if (beatmap.HitObjects.Count == 0)

View File

@ -101,9 +101,8 @@ namespace osu.Game.Screens.Edit
return;
}
AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap));
AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap, Beatmap.Value.Skin));
dependencies.CacheAs(editorBeatmap);
changeHandler = new EditorChangeHandler(editorBeatmap);
dependencies.CacheAs<IEditorChangeHandler>(changeHandler);
@ -399,7 +398,7 @@ namespace osu.Game.Screens.Edit
clock.SeekForward(!clock.IsRunning, amount);
}
private void saveBeatmap() => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap);
private void saveBeatmap() => beatmapManager.Save(playableBeatmap.BeatmapInfo, editorBeatmap, editorBeatmap.BeatmapSkin);
private void exportBeatmap()
{

View File

@ -15,6 +15,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Skinning;
namespace osu.Game.Screens.Edit
{
@ -47,6 +48,8 @@ namespace osu.Game.Screens.Edit
public readonly IBeatmap PlayableBeatmap;
public readonly ISkin BeatmapSkin;
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
@ -54,9 +57,10 @@ namespace osu.Game.Screens.Edit
private readonly Dictionary<HitObject, Bindable<double>> startTimeBindables = new Dictionary<HitObject, Bindable<double>>();
public EditorBeatmap(IBeatmap playableBeatmap)
public EditorBeatmap(IBeatmap playableBeatmap, ISkin beatmapSkin = null)
{
PlayableBeatmap = playableBeatmap;
BeatmapSkin = beatmapSkin;
beatmapProcessor = playableBeatmap.BeatmapInfo.Ruleset?.CreateInstance().CreateBeatmapProcessor(PlayableBeatmap);

View File

@ -85,7 +85,7 @@ namespace osu.Game.Screens.Edit
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
new LegacyBeatmapEncoder(editorBeatmap).Encode(sw);
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
savedStates.Add(stream.ToArray());
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Skinning
public readonly int Keys;
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
public Dictionary<string, Color4> CustomColours { get; } = new Dictionary<string, Color4>();
public Dictionary<string, string> ImageLookups = new Dictionary<string, string>();

View File

@ -45,7 +45,7 @@ namespace osu.Game.Skinning
public void AddComboColours(params Color4[] colours) => comboColours.AddRange(colours);
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
public Dictionary<string, Color4> CustomColours { get; } = new Dictionary<string, Color4>();
public readonly Dictionary<string, string> ConfigDictionary = new Dictionary<string, string>();
}