summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Config.cs
blob: 326685c103c34ec47d77647696eb3935de7840b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
using System;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StardewModdingAPI.Framework;

namespace StardewModdingAPI
{
    /// <summary>A dynamic configuration class for a mod.</summary>
    [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
    public abstract class Config
    {
        /*********
        ** Accessors
        *********/
        /// <summary>The full path to the configuration file.</summary>
        [JsonIgnore]
        public virtual string ConfigLocation { get; protected internal set; }

        /// <summary>The directory path containing the configuration file.</summary>
        [JsonIgnore]
        public virtual string ConfigDir => Path.GetDirectoryName(this.ConfigLocation);


        /*********
        ** Public methods
        *********/
        /// <summary>Construct an instance of the config class.</summary>
        /// <typeparam name="T">The config class type.</typeparam>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public virtual Config Instance<T>() where T : Config => Activator.CreateInstance<T>();

        /// <summary>Load the config from the JSON file, saving it to disk if needed.</summary>
        /// <typeparam name="T">The config class type.</typeparam>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public virtual T LoadConfig<T>() where T : Config
        {
            // validate
            if (string.IsNullOrEmpty(this.ConfigLocation))
            {
                Log.AsyncR("A config tried to load without specifying a location on the disk.");
                return null;
            }

            // read or generate config
            T returnValue;
            if (!File.Exists(this.ConfigLocation))
            {
                T config = this.GenerateDefaultConfig<T>();
                config.ConfigLocation = this.ConfigLocation;
                returnValue = config;
            }
            else
            {
                try
                {
                    T config = JsonConvert.DeserializeObject<T>(File.ReadAllText(this.ConfigLocation));
                    config.ConfigLocation = this.ConfigLocation;
                    returnValue = config.UpdateConfig<T>();
                }
                catch (Exception ex)
                {
                    Log.AsyncR($"Invalid JSON ({this.GetType().Name}): {this.ConfigLocation} \n{ex}");
                    return this.GenerateDefaultConfig<T>();
                }
            }

            returnValue.WriteConfig();
            return returnValue;
        }

        /// <summary>Get the default config values.</summary>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public abstract T GenerateDefaultConfig<T>() where T : Config;

        /// <summary>Get the current configuration with missing values defaulted.</summary>
        /// <typeparam name="T">The config class type.</typeparam>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public virtual T UpdateConfig<T>() where T : Config
        {
            try
            {
                // get default + user config
                JObject defaultConfig = JObject.FromObject(this.Instance<T>().GenerateDefaultConfig<T>());
                JObject currentConfig = JObject.FromObject(this);
                defaultConfig.Merge(currentConfig, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace });

                // cast json object to config
                T config = defaultConfig.ToObject<T>();

                // update location
                config.ConfigLocation = this.ConfigLocation;

                return config;
            }
            catch (Exception ex)
            {
                Log.AsyncR($"An error occured when updating a config: {ex}");
                return this as T;
            }
        }


        /*********
        ** Protected methods
        *********/
        /// <summary>Construct an instance.</summary>
        protected Config()
        {
            Program.DeprecationManager.Warn("the Config class", "1.0", DeprecationLevel.Notice);
            Program.DeprecationManager.MarkWarned($"{nameof(Mod)}.{nameof(Mod.BaseConfigPath)}", "1.0"); // typically used to construct config, avoid redundant warnings
        }
    }

    /// <summary>Provides extension methods for <see cref="Config"/> classes.</summary>
    [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
    public static class ConfigExtensions
    {
        /// <summary>Initialise the configuration. That includes loading, saving, and merging the config file and in memory at a default state. This method should not be used to reload or to resave a config. NOTE: You MUST set your config EQUAL to the return of this method!</summary>
        /// <typeparam name="T">The config class type.</typeparam>
        /// <param name="baseConfig">The base configuration to initialise.</param>
        /// <param name="configLocation">The base configuration file path.</param>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public static T InitializeConfig<T>(this T baseConfig, string configLocation) where T : Config
        {
            if (baseConfig == null)
                baseConfig = Activator.CreateInstance<T>();

            if (string.IsNullOrEmpty(configLocation))
            {
                Log.AsyncR("A config tried to initialize without specifying a location on the disk.");
                return null;
            }

            baseConfig.ConfigLocation = configLocation;
            return baseConfig.LoadConfig<T>();
        }

        /// <summary>Writes the configuration to the JSON file.</summary>
        /// <typeparam name="T">The config class type.</typeparam>
        /// <param name="baseConfig">The base configuration to initialise.</param>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public static void WriteConfig<T>(this T baseConfig) where T : Config
        {
            if (string.IsNullOrEmpty(baseConfig?.ConfigLocation) || string.IsNullOrEmpty(baseConfig.ConfigDir))
            {
                Log.AsyncR("A config attempted to save when it itself or it's location were null.");
                return;
            }

            string json = JsonConvert.SerializeObject(baseConfig, Formatting.Indented);
            if (!Directory.Exists(baseConfig.ConfigDir))
                Directory.CreateDirectory(baseConfig.ConfigDir);

            if (!File.Exists(baseConfig.ConfigLocation) || !File.ReadAllText(baseConfig.ConfigLocation).SequenceEqual(json))
                File.WriteAllText(baseConfig.ConfigLocation, json);
        }

        /// <summary>Rereads the JSON file and merges its values with a default config. NOTE: You MUST set your config EQUAL to the return of this method!</summary>
        /// <typeparam name="T">The config class type.</typeparam>
        /// <param name="baseConfig">The base configuration to initialise.</param>
        [Obsolete("This base class is obsolete since SMAPI 1.0. See the latest project README for details.")]
        public static T ReloadConfig<T>(this T baseConfig) where T : Config
        {
            return baseConfig.LoadConfig<T>();
        }
    }
}