mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 10:43:04 +08:00
Add mod setting (de)serialization support
This commit is contained in:
parent
86ae442d54
commit
2bc7458abf
82
osu.Game.Tests/Online/TestAPIModSerialization.cs
Normal file
82
osu.Game.Tests/Online/TestAPIModSerialization.cs
Normal file
@ -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.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAPIModSerialization
|
||||
{
|
||||
[Test]
|
||||
public void TestAcronymIsPreserved()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod());
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
|
||||
Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRawSettingIsPreserved()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
|
||||
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvertedModHasCorrectSetting()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var converted = (TestMod)deserialized.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted.TestSetting.Value, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
private class TestRuleset : Ruleset
|
||||
{
|
||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new[] { new TestMod() };
|
||||
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
||||
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
|
||||
|
||||
public override string Description { get; }
|
||||
public override string ShortName { get; }
|
||||
}
|
||||
|
||||
private class TestMod : Mod
|
||||
{
|
||||
public override string Name => "Test Mod";
|
||||
public override string Acronym => "TM";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Test")]
|
||||
public BindableNumber<double> TestSetting { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Default = 5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -35,16 +35,11 @@ namespace osu.Game.Configuration
|
||||
{
|
||||
public static IEnumerable<Drawable> CreateSettingsControls(this object obj)
|
||||
{
|
||||
foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
|
||||
foreach (var (attr, property) in obj.GetSettingsSourceProperties())
|
||||
{
|
||||
var attr = property.GetCustomAttribute<SettingSourceAttribute>(true);
|
||||
object value = property.GetValue(obj);
|
||||
|
||||
if (attr == null)
|
||||
continue;
|
||||
|
||||
var prop = property.GetValue(obj);
|
||||
|
||||
switch (prop)
|
||||
switch (value)
|
||||
{
|
||||
case BindableNumber<float> bNumber:
|
||||
yield return new SettingsSlider<float>
|
||||
@ -102,9 +97,22 @@ namespace osu.Game.Configuration
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})");
|
||||
throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({value})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<(SettingSourceAttribute, PropertyInfo)> GetSettingsSourceProperties(this object obj)
|
||||
{
|
||||
foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
var attr = property.GetCustomAttribute<SettingSourceAttribute>(true);
|
||||
|
||||
if (attr == null)
|
||||
continue;
|
||||
|
||||
yield return (attr, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
56
osu.Game/Online/API/APIMod.cs
Normal file
56
osu.Game/Online/API/APIMod.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// 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 Humanizer;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
public class APIMod : IMod
|
||||
{
|
||||
[JsonProperty("acronym")]
|
||||
public string Acronym { get; set; }
|
||||
|
||||
[JsonProperty("settings")]
|
||||
public Dictionary<string, object> Settings { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
[JsonConstructor]
|
||||
private APIMod()
|
||||
{
|
||||
}
|
||||
|
||||
public APIMod(Mod mod)
|
||||
{
|
||||
Acronym = mod.Acronym;
|
||||
|
||||
foreach (var (_, property) in mod.GetSettingsSourceProperties())
|
||||
Settings.Add(property.Name.Underscore(), property.GetValue(mod));
|
||||
}
|
||||
|
||||
public Mod ToMod(Ruleset ruleset)
|
||||
{
|
||||
Mod resultMod = ruleset.GetAllMods().FirstOrDefault(m => m.Acronym == Acronym);
|
||||
|
||||
if (resultMod == null)
|
||||
return null; // Todo: Maybe throw exception?
|
||||
|
||||
foreach (var (_, property) in resultMod.GetSettingsSourceProperties())
|
||||
{
|
||||
if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue))
|
||||
continue;
|
||||
|
||||
((IBindable)property.GetValue(resultMod)).Parse(settingValue);
|
||||
}
|
||||
|
||||
return resultMod;
|
||||
}
|
||||
|
||||
public bool Equals(IMod other) => Acronym == other?.Acronym;
|
||||
}
|
||||
}
|
@ -1,14 +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.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
public class APIMod : IMod
|
||||
{
|
||||
public string Acronym { get; set; }
|
||||
|
||||
public bool Equals(IMod other) => Acronym == other?.Acronym;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -50,7 +51,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
[JsonProperty("allowed_mods")]
|
||||
private APIMod[] allowedMods
|
||||
{
|
||||
get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
||||
get => AllowedMods.Select(m => new APIMod(m)).ToArray();
|
||||
set => allowedModsBacking = value;
|
||||
}
|
||||
|
||||
@ -59,7 +60,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
[JsonProperty("required_mods")]
|
||||
private APIMod[] requiredMods
|
||||
{
|
||||
get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
||||
get => RequiredMods.Select(m => new APIMod(m)).ToArray();
|
||||
set => requiredModsBacking = value;
|
||||
}
|
||||
|
||||
@ -72,10 +73,12 @@ namespace osu.Game.Online.Multiplayer
|
||||
Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets);
|
||||
Ruleset = rulesets.GetRuleset(RulesetID);
|
||||
|
||||
Ruleset rulesetInstance = Ruleset.CreateInstance();
|
||||
|
||||
if (allowedModsBacking != null)
|
||||
{
|
||||
AllowedMods.Clear();
|
||||
AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => allowedModsBacking.Any(m => m.Acronym == mod.Acronym)));
|
||||
AllowedMods.AddRange(allowedModsBacking.Select(m => m.ToMod(rulesetInstance)));
|
||||
|
||||
allowedModsBacking = null;
|
||||
}
|
||||
@ -83,7 +86,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
if (requiredModsBacking != null)
|
||||
{
|
||||
RequiredMods.Clear();
|
||||
RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => requiredModsBacking.Any(m => m.Acronym == mod.Acronym)));
|
||||
RequiredMods.AddRange(requiredModsBacking.Select(m => m.ToMod(rulesetInstance)));
|
||||
|
||||
requiredModsBacking = null;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user