216 lines
8.6 KiB
C#
216 lines
8.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEditor.SettingsManagement
|
|
{
|
|
/// <summary>
|
|
/// Represents a settings repository that stores data serialized to a JSON file.
|
|
/// </summary>
|
|
[Serializable]
|
|
public class FileSettingsRepository : ISettingsRepository
|
|
{
|
|
/// <summary>
|
|
/// Location of where the package settings are saved under the `ProjectSettings` directory.
|
|
/// </summary>
|
|
/// <returns>The folder where package settings are saved under the `ProjectSettings` directory.</returns>
|
|
protected const string k_PackageSettingsDirectory = "ProjectSettings/Packages";
|
|
|
|
/// <summary>
|
|
/// Location of where the package settings are saved under the `UserSettings` directory.
|
|
/// </summary>
|
|
/// <returns>Per-project user settings directory. </returns>
|
|
protected const string k_UserProjectSettingsDirectory = "UserSettings/Packages";
|
|
|
|
const bool k_PrettyPrintJson = true;
|
|
|
|
bool m_Initialized;
|
|
string m_Path;
|
|
[SerializeField]
|
|
SettingsDictionary m_Dictionary = new SettingsDictionary();
|
|
Hash128 m_JsonHash;
|
|
|
|
/// <summary>
|
|
/// Initializes and returns an instance of the FileSettingsRepository
|
|
/// with the serialized data location set to the specified path.
|
|
/// </summary>
|
|
/// <param name="path">The project-relative path to save settings to.</param>
|
|
public FileSettingsRepository(string path)
|
|
{
|
|
m_Path = path;
|
|
m_Initialized = false;
|
|
AssemblyReloadEvents.beforeAssemblyReload += Save;
|
|
EditorApplication.quitting += Save;
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
if (m_Initialized)
|
|
return;
|
|
|
|
m_Initialized = true;
|
|
|
|
if (TryLoadSavedJson(out string json))
|
|
{
|
|
m_Dictionary = null;
|
|
m_JsonHash = Hash128.Compute(json);
|
|
EditorJsonUtility.FromJsonOverwrite(json, this);
|
|
}
|
|
|
|
if (m_Dictionary == null)
|
|
m_Dictionary = new SettingsDictionary();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the <see cref="SettingsScope"/> this repository applies to.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// By default, this repository implementation is relevant to the Project scope, but any implementations
|
|
/// that override this method can choose to store this serialized data at a user scope instead.
|
|
/// </remarks>
|
|
/// <value>
|
|
/// <see cref="SettingsScope.Project"/>, meaning that this setting applies to project settings (the default);
|
|
/// or <see cref="SettingsScope.User"/>, meaning that this setting applies to user preferences.
|
|
/// </value>
|
|
/// <seealso cref="ISettingsRepository.scope"/>
|
|
public virtual SettingsScope scope => SettingsScope.Project;
|
|
|
|
/// <summary>
|
|
/// Gets the full path to the file containing the serialized settings data.
|
|
/// </summary>
|
|
/// <value>The location stored for this repository.</value>
|
|
/// <seealso cref="ISettingsRepository.path"/>
|
|
public string path
|
|
{
|
|
get { return m_Path; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the name of file containing the serialized settings data.
|
|
/// </summary>
|
|
/// <value>The bare filename of the settings file.</value>
|
|
public string name => Path.GetFileNameWithoutExtension(path);
|
|
|
|
/// <summary>
|
|
/// Loads the JSON file that stores the values for this settings object.
|
|
/// </summary>
|
|
/// <param name="json">The full path to the JSON file to load.</param>
|
|
/// <returns>True if the file exists; false if it doesn't.</returns>
|
|
public bool TryLoadSavedJson(out string json)
|
|
{
|
|
json = string.Empty;
|
|
if (!File.Exists(path))
|
|
return false;
|
|
json = File.ReadAllText(path);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves all settings to their serialized state.
|
|
/// </summary>
|
|
/// <seealso cref="ISettingsRepository.Save"/>
|
|
public void Save()
|
|
{
|
|
Init();
|
|
|
|
if (!File.Exists(path))
|
|
{
|
|
var directory = Path.GetDirectoryName(path);
|
|
|
|
if (string.IsNullOrEmpty(directory))
|
|
{
|
|
Debug.LogError(
|
|
$"Settings file {name} is saved to an invalid path: {path}. Settings will not be saved.");
|
|
return;
|
|
}
|
|
|
|
Directory.CreateDirectory(directory);
|
|
}
|
|
|
|
string json = EditorJsonUtility.ToJson(this, k_PrettyPrintJson);
|
|
|
|
// While unlikely, a hash collision is possible. Always test the actual saved contents before early exit.
|
|
if (m_JsonHash == Hash128.Compute(json)
|
|
&& TryLoadSavedJson(out string existing)
|
|
&& existing.Equals(json))
|
|
return;
|
|
|
|
#if UNITY_2019_3_OR_NEWER
|
|
// AssetDatabase.IsOpenForEdit can be a very slow synchronous blocking call when Unity is connected to
|
|
// Perforce Version Control. Especially if it's called repeatedly with every EditorGUI redraw.
|
|
if (File.Exists(path) && !AssetDatabase.IsOpenForEdit(path))
|
|
{
|
|
if (!AssetDatabase.MakeEditable(path))
|
|
{
|
|
Debug.LogWarning($"Could not save package settings to {path}");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
try
|
|
{
|
|
m_JsonHash = Hash128.Compute(json);
|
|
File.WriteAllText(path, json);
|
|
}
|
|
catch (UnauthorizedAccessException)
|
|
{
|
|
Debug.LogWarning($"Could not save package settings to {path}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a value for a settings entry with a matching key and type `T`.
|
|
/// </summary>
|
|
/// <param name="key">The key used to identify the settings entry.</param>
|
|
/// <param name="value">The value to set. This value must be serializable.</param>
|
|
/// <typeparam name="T">The type of value that this key points to.</typeparam>
|
|
/// <seealso cref="ISettingsRepository.Set{T}"/>
|
|
public void Set<T>(string key, T value)
|
|
{
|
|
Init();
|
|
m_Dictionary.Set<T>(key, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a value with key of type `T`, or the fallback value if no matching key is found.
|
|
/// </summary>
|
|
/// <param name="key">The key used to identify the settings entry.</param>
|
|
/// <param name="fallback">Specify the value of type `T` to return if the entry can't be found.</param>
|
|
/// <typeparam name="T">The type of value that this key points to.</typeparam>
|
|
/// <returns>The settings value if a match is found; otherwise, it returns the default (fallback) value.</returns>
|
|
/// <seealso cref="ISettingsRepository.Get{T}"/>
|
|
public T Get<T>(string key, T fallback = default(T))
|
|
{
|
|
Init();
|
|
return m_Dictionary.Get<T>(key, fallback);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether this repository contains a settings entry that matches the specified key and is of type `T`.
|
|
/// </summary>
|
|
/// <param name="key">The key used to identify the settings entry.</param>
|
|
/// <typeparam name="T">The type of value that this key points to.</typeparam>
|
|
/// <returns>True if a match is found for both key and type; false if no entry is found.</returns>
|
|
/// <seealso cref="ISettingsRepository.ContainsKey{T}"/>
|
|
public bool ContainsKey<T>(string key)
|
|
{
|
|
Init();
|
|
return m_Dictionary.ContainsKey<T>(key);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a key-value pair from the settings repository. This method identifies the settings entry to remove
|
|
/// by matching the specified key for a value of type `T`.
|
|
/// </summary>
|
|
/// <param name="key">The key used to identify the settings entry.</param>
|
|
/// <typeparam name="T">The type of value that this key points to.</typeparam>
|
|
/// <seealso cref="ISettingsRepository.Remove{T}"/>
|
|
public void Remove<T>(string key)
|
|
{
|
|
Init();
|
|
m_Dictionary.Remove<T>(key);
|
|
}
|
|
}
|
|
}
|