// Copyright (c) ppy Pty Ltd . 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 Humanizer; using MessagePack; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Utils; namespace osu.Game.Online.API { [MessagePackObject] public class APIMod : IEquatable { [JsonProperty("acronym")] [Key(0)] public string Acronym { get; set; } [JsonProperty("settings")] [Key(1)] [MessagePackFormatter(typeof(ModSettingsDictionaryFormatter))] public Dictionary Settings { get; set; } = new Dictionary(); [JsonConstructor] [SerializationConstructor] public APIMod() { } public APIMod(Mod mod) { Acronym = mod.Acronym; foreach (var (_, property) in mod.GetSettingsSourceProperties()) { var bindable = (IBindable)property.GetValue(mod); if (!bindable.IsDefault) Settings.Add(property.Name.Underscore(), ModUtils.GetSettingUnderlyingValue(bindable)); } } public Mod ToMod(Ruleset ruleset) { Mod resultMod = ruleset.CreateModFromAcronym(Acronym); if (resultMod == null) { Logger.Log($"There is no mod in the ruleset ({ruleset.ShortName}) matching the acronym {Acronym}."); return new UnknownMod(Acronym); } if (Settings.Count > 0) { foreach (var (_, property) in resultMod.GetSettingsSourceProperties()) { if (!Settings.TryGetValue(property.Name.Underscore(), out object settingValue)) continue; resultMod.CopyAdjustedSetting((IBindable)property.GetValue(resultMod), settingValue); } } return resultMod; } public bool Equals(APIMod other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Acronym == other.Acronym && Settings.SequenceEqual(other.Settings, ModSettingsEqualityComparer.Default); } public override string ToString() { if (Settings.Count > 0) return $"{Acronym} ({string.Join(',', Settings.Select(kvp => $"{kvp.Key}:{kvp.Value}"))})"; return $"{Acronym}"; } private class ModSettingsEqualityComparer : IEqualityComparer> { public static ModSettingsEqualityComparer Default { get; } = new ModSettingsEqualityComparer(); public bool Equals(KeyValuePair x, KeyValuePair y) { object xValue = ModUtils.GetSettingUnderlyingValue(x.Value); object yValue = ModUtils.GetSettingUnderlyingValue(y.Value); return x.Key == y.Key && EqualityComparer.Default.Equals(xValue, yValue); } public int GetHashCode(KeyValuePair obj) => HashCode.Combine(obj.Key, ModUtils.GetSettingUnderlyingValue(obj.Value)); } } }