502 lines
20 KiB
C#
502 lines
20 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
public static class SettingsManager
|
||
{
|
||
public static event Action SettingsSaved;
|
||
public static event Action OnSettingsChanged;
|
||
|
||
private static string SettingsFileName = "GameSettings.json";
|
||
private static string SettingsPath => Application.persistentDataPath;
|
||
public static SettingsSchemeDTO currentSchemeDTO { get; private set; }
|
||
|
||
// Обновленный метод Initialize с обработкой ошибок загрузки
|
||
public static void Initialize(SettingsScheme defaultSchemeAsset)
|
||
{
|
||
LoadSettings(defaultSchemeAsset);
|
||
}
|
||
|
||
#region Load/Save/Restore
|
||
private static void LoadSettings(SettingsScheme defaultSchemeAsset)
|
||
{
|
||
try
|
||
{
|
||
if (defaultSchemeAsset == null)
|
||
{
|
||
Debug.LogError("Default scheme asset is null!");
|
||
return;
|
||
}
|
||
|
||
// Конвертируем дефолтную схему в DTO
|
||
var defaultDTO = defaultSchemeAsset.GetDTO();
|
||
|
||
if (defaultDTO == null)
|
||
{
|
||
Debug.LogError("Failed to convert default scheme to DTO!");
|
||
return;
|
||
}
|
||
|
||
SettingsSchemeDTO loadedDTO = null;
|
||
bool loadSuccess = false;
|
||
|
||
try
|
||
{
|
||
var loadResult = FileUtil.LoadJson<SettingsSchemeDTO>(SettingsFileName, SettingsPath);
|
||
Debug.Log(loadResult.Message);
|
||
|
||
if (loadResult.OK && loadResult.Data != null)
|
||
{
|
||
loadedDTO = loadResult.Data;
|
||
loadSuccess = true;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Error loading settings file: {ex.Message}");
|
||
}
|
||
|
||
if (!loadSuccess || loadedDTO == null)
|
||
{
|
||
currentSchemeDTO = CloneSettingsScheme(defaultDTO);
|
||
Debug.Log("Загружена схема настроек по умолчанию (файл отсутствует или поврежден)");
|
||
SaveSettings();
|
||
return;
|
||
}
|
||
|
||
string defaultHash = defaultDTO.CalculateHash();
|
||
string loadedHash = loadedDTO.CalculateHash();
|
||
|
||
if (string.Compare(defaultHash, loadedHash, StringComparison.Ordinal) == 0)
|
||
{
|
||
currentSchemeDTO = loadedDTO;
|
||
Debug.Log("Загружена схема настроек из файла");
|
||
return;
|
||
}
|
||
|
||
// Хэши не совпадают - восстанавливаем совместимые настройки
|
||
Debug.Log("Обнаружено несоответствие схем. Выполняется восстановление настроек...");
|
||
currentSchemeDTO = RestoreSettings(defaultDTO, loadedDTO);
|
||
|
||
// Проверяем, что восстановление прошло успешно
|
||
if (currentSchemeDTO == null)
|
||
{
|
||
Debug.LogError("Restore settings failed! Using default settings.");
|
||
currentSchemeDTO = CloneSettingsScheme(defaultDTO);
|
||
}
|
||
|
||
SaveSettings();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Critical error in Initialize: {ex.Message}\n{ex.StackTrace}");
|
||
// Последняя попытка - создать дефолтную схему
|
||
if (defaultSchemeAsset != null)
|
||
{
|
||
currentSchemeDTO = defaultSchemeAsset.GetDTO();
|
||
SaveSettings();
|
||
}
|
||
}
|
||
}
|
||
|
||
private static SettingsSchemeDTO RestoreSettings(SettingsSchemeDTO defaultDTO, SettingsSchemeDTO loadedDTO)
|
||
{
|
||
try
|
||
{
|
||
// Проверяем входные параметры
|
||
if (defaultDTO == null)
|
||
{
|
||
Debug.LogError("defaultDTO is null! Cannot restore settings.");
|
||
return null;
|
||
}
|
||
|
||
if (loadedDTO == null)
|
||
{
|
||
Debug.LogWarning("loadedDTO is null, using default settings");
|
||
return CloneSettingsScheme(defaultDTO);
|
||
}
|
||
|
||
// Проверяем, что у defaultDTO есть группы
|
||
if (defaultDTO.groups == null || defaultDTO.groups.Count == 0)
|
||
{
|
||
Debug.LogError("defaultDTO has no groups! Cannot restore settings.");
|
||
return CloneSettingsScheme(defaultDTO);
|
||
}
|
||
|
||
var restoredDTO = new SettingsSchemeDTO();
|
||
restoredDTO.groups = new List<SettingsGroupDTO>();
|
||
|
||
// Создаем словарь для быстрого поиска групп и настроек из загруженной схемы
|
||
var loadedGroupsMap = new Dictionary<string, SettingsGroupDTO>();
|
||
if (loadedDTO.groups != null)
|
||
{
|
||
foreach (var group in loadedDTO.groups)
|
||
{
|
||
if (group != null && !string.IsNullOrEmpty(group.Name))
|
||
{
|
||
// Если группа с таким именем уже есть, пропускаем дубликат
|
||
if (!loadedGroupsMap.ContainsKey(group.Name))
|
||
{
|
||
loadedGroupsMap[group.Name] = group;
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"Duplicate group name found: {group.Name}, skipping duplicate");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Проходим по каждой группе в дефолтной схеме
|
||
foreach (var defaultGroup in defaultDTO.groups)
|
||
{
|
||
if (defaultGroup == null)
|
||
{
|
||
Debug.LogWarning("Null group found in defaultDTO, skipping");
|
||
continue;
|
||
}
|
||
|
||
try
|
||
{
|
||
var restoredGroup = new SettingsGroupDTO();
|
||
restoredGroup.Name = defaultGroup.Name;
|
||
restoredGroup.IconPath = defaultGroup.IconPath;
|
||
restoredGroup.Settings = new List<SettingDTO>();
|
||
|
||
// Создаем словарь настроек загруженной группы (если она существует)
|
||
var loadedSettingsMap = new Dictionary<string, SettingDTO>();
|
||
if (loadedGroupsMap.TryGetValue(defaultGroup.Name, out var loadedGroup) && loadedGroup != null)
|
||
{
|
||
if (loadedGroup.Settings != null)
|
||
{
|
||
foreach (var setting in loadedGroup.Settings)
|
||
{
|
||
if (setting != null && !string.IsNullOrEmpty(setting.Id))
|
||
{
|
||
try
|
||
{
|
||
string settingHash = setting.GetStringForHash();
|
||
if (!loadedSettingsMap.ContainsKey(settingHash))
|
||
{
|
||
loadedSettingsMap[settingHash] = setting;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Error calculating hash for setting {setting.Id}: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Проходим по каждой настройке в дефолтной группе
|
||
if (defaultGroup.Settings != null)
|
||
{
|
||
foreach (var defaultSetting in defaultGroup.Settings)
|
||
{
|
||
if (defaultSetting == null)
|
||
{
|
||
Debug.LogWarning("Null setting found in default group, skipping");
|
||
continue;
|
||
}
|
||
|
||
try
|
||
{
|
||
string defaultSettingHash = defaultSetting.GetStringForHash();
|
||
SettingDTO finalSetting = null;
|
||
|
||
// Пытаемся найти совместимую настройку
|
||
if (loadedSettingsMap.TryGetValue(defaultSettingHash, out var compatibleSetting))
|
||
{
|
||
// Нашли - берем значение из загруженной настройки
|
||
finalSetting = CopySettingValue(compatibleSetting, defaultSetting);
|
||
Debug.Log($"Восстановлена настройка: {defaultSetting.Id} из группы {defaultGroup.Name}");
|
||
}
|
||
else
|
||
{
|
||
// Не нашли - берем значение по умолчанию
|
||
finalSetting = CopySettingStructure(defaultSetting);
|
||
Debug.Log($"Использовано значение по умолчанию для настройки: {defaultSetting.Id} из группы {defaultGroup.Name}");
|
||
}
|
||
|
||
if (finalSetting != null)
|
||
{
|
||
restoredGroup.Settings.Add(finalSetting);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Error restoring setting {defaultSetting.Id} from group {defaultGroup.Name}: {ex.Message}");
|
||
// Добавляем настройку по умолчанию в случае ошибки
|
||
var fallbackSetting = CopySettingStructure(defaultSetting);
|
||
if (fallbackSetting != null)
|
||
{
|
||
restoredGroup.Settings.Add(fallbackSetting);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
restoredDTO.groups.Add(restoredGroup);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Error restoring group {defaultGroup.Name}: {ex.Message}");
|
||
// Пытаемся добавить группу с настройками по умолчанию
|
||
try
|
||
{
|
||
var fallbackGroup = CopyGroupStructure(defaultGroup);
|
||
if (fallbackGroup != null)
|
||
{
|
||
restoredDTO.groups.Add(fallbackGroup);
|
||
}
|
||
}
|
||
catch (Exception fallbackEx)
|
||
{
|
||
Debug.LogError($"Failed to create fallback group {defaultGroup.Name}: {fallbackEx.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// Проверяем, что у нас есть хотя бы одна группа
|
||
if (restoredDTO.groups == null || restoredDTO.groups.Count == 0)
|
||
{
|
||
Debug.LogError("Failed to restore any groups! Using default settings.");
|
||
return CloneSettingsScheme(defaultDTO);
|
||
}
|
||
|
||
Debug.Log($"Настройки восстановлены успешно. Групп: {restoredDTO.groups.Count}");
|
||
return restoredDTO;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Critical error in RestoreSettings: {ex.Message}\n{ex.StackTrace}");
|
||
// В случае критической ошибки возвращаем копию дефолтной схемы
|
||
return CloneSettingsScheme(defaultDTO);
|
||
}
|
||
}
|
||
|
||
// Вспомогательный метод для создания глубокой копии схемы
|
||
private static SettingsSchemeDTO CloneSettingsScheme(SettingsSchemeDTO source)
|
||
{
|
||
if (source == null) return null;
|
||
|
||
try
|
||
{
|
||
var clone = new SettingsSchemeDTO();
|
||
clone.groups = new List<SettingsGroupDTO>();
|
||
|
||
if (source.groups != null)
|
||
{
|
||
foreach (var group in source.groups)
|
||
{
|
||
if (group != null)
|
||
{
|
||
var groupClone = CopyGroupStructure(group);
|
||
if (groupClone != null)
|
||
{
|
||
clone.groups.Add(groupClone);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return clone;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Error cloning settings scheme: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// Вспомогательный метод для копирования группы
|
||
private static SettingsGroupDTO CopyGroupStructure(SettingsGroupDTO source)
|
||
{
|
||
if (source == null) return null;
|
||
|
||
try
|
||
{
|
||
var group = new SettingsGroupDTO();
|
||
group.Name = source.Name;
|
||
group.IconPath = source.IconPath;
|
||
group.Settings = new List<SettingDTO>();
|
||
|
||
if (source.Settings != null)
|
||
{
|
||
foreach (var setting in source.Settings)
|
||
{
|
||
if (setting != null)
|
||
{
|
||
var settingClone = CopySettingStructure(setting);
|
||
if (settingClone != null)
|
||
{
|
||
group.Settings.Add(settingClone);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return group;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"Error copying group {source?.Name}: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private static SettingDTO CopySettingValue(SettingDTO source, SettingDTO targetTemplate)
|
||
{
|
||
var newSetting = new SettingDTO();
|
||
|
||
// Копируем структуру из шаблона
|
||
newSetting.Type = targetTemplate.Type;
|
||
newSetting.Id = targetTemplate.Id;
|
||
newSetting.Alias = targetTemplate.Alias;
|
||
newSetting.Description = targetTemplate.Description;
|
||
newSetting.IntMin = targetTemplate.IntMin;
|
||
newSetting.IntMax = targetTemplate.IntMax;
|
||
newSetting.IntStep = targetTemplate.IntStep;
|
||
newSetting.FloatMin = targetTemplate.FloatMin;
|
||
newSetting.FloatMax = targetTemplate.FloatMax;
|
||
newSetting.FloatStep = targetTemplate.FloatStep;
|
||
|
||
if (targetTemplate.AllowedValues != null)
|
||
{
|
||
newSetting.AllowedValues = new List<string>(targetTemplate.AllowedValues);
|
||
}
|
||
|
||
// Копируем значение из source
|
||
switch (targetTemplate.Type)
|
||
{
|
||
case Setting.SettingType.Bool:
|
||
newSetting.BoolValue = source.BoolValue;
|
||
break;
|
||
case Setting.SettingType.Int:
|
||
newSetting.IntValue = Mathf.Clamp(source.IntValue, targetTemplate.IntMin, targetTemplate.IntMax);
|
||
break;
|
||
case Setting.SettingType.Float:
|
||
newSetting.FloatValue = Mathf.Clamp(source.FloatValue, targetTemplate.FloatMin, targetTemplate.FloatMax);
|
||
break;
|
||
case Setting.SettingType.String:
|
||
if (targetTemplate.AllowedValues != null && targetTemplate.AllowedValues.Contains(source.StringValue))
|
||
{
|
||
newSetting.StringValue = source.StringValue;
|
||
}
|
||
else
|
||
{
|
||
newSetting.StringValue = targetTemplate.StringValue;
|
||
}
|
||
break;
|
||
case Setting.SettingType.Signal:
|
||
newSetting.SignalValue = targetTemplate.SignalValue;
|
||
break;
|
||
}
|
||
|
||
return newSetting;
|
||
}
|
||
|
||
private static SettingDTO CopySettingStructure(SettingDTO template)
|
||
{
|
||
var newSetting = new SettingDTO();
|
||
|
||
newSetting.Type = template.Type;
|
||
newSetting.Id = template.Id;
|
||
newSetting.Alias = template.Alias;
|
||
newSetting.Description = template.Description;
|
||
newSetting.IntMin = template.IntMin;
|
||
newSetting.IntMax = template.IntMax;
|
||
newSetting.IntStep = template.IntStep;
|
||
newSetting.FloatMin = template.FloatMin;
|
||
newSetting.FloatMax = template.FloatMax;
|
||
newSetting.FloatStep = template.FloatStep;
|
||
newSetting.BoolValue = template.BoolValue;
|
||
newSetting.IntValue = template.IntValue;
|
||
newSetting.FloatValue = template.FloatValue;
|
||
newSetting.StringValue = template.StringValue;
|
||
newSetting.SignalValue = template.SignalValue;
|
||
|
||
if (template.AllowedValues != null)
|
||
{
|
||
newSetting.AllowedValues = new List<string>(template.AllowedValues);
|
||
}
|
||
|
||
return newSetting;
|
||
}
|
||
|
||
public static void SaveSettings()
|
||
{
|
||
if (currentSchemeDTO != null)
|
||
{
|
||
FileUtil.SaveJson(SettingsFileName, SettingsPath, currentSchemeDTO);
|
||
SettingsSaved?.Invoke();
|
||
Debug.Log("Настройки сохранены");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Get Values
|
||
|
||
private static T GetSetting<T>(string id, Setting.SettingType expectedType, Func<SettingDTO, T> getValue)
|
||
{
|
||
if (!IsInitialized())
|
||
throw new InvalidOperationException("Менеджер настроек не инициализирован");
|
||
|
||
var setting = FindSetting(id);
|
||
|
||
if (setting == null)
|
||
throw new InvalidOperationException($"Настройка {id} не найдена");
|
||
|
||
if (setting.Type != expectedType)
|
||
throw new InvalidOperationException($"Настройка {id} не соответствует ожидаемому типу {expectedType}");
|
||
|
||
return getValue(setting);
|
||
}
|
||
|
||
public static string GetString(string id)
|
||
=> GetSetting(id, Setting.SettingType.String, s => s.StringValue);
|
||
|
||
public static int GetInt(string id)
|
||
=> GetSetting(id, Setting.SettingType.Int, s => s.IntValue);
|
||
|
||
public static float GetFloat(string id)
|
||
=> GetSetting(id, Setting.SettingType.Float, s => s.FloatValue);
|
||
|
||
public static bool GetBool(string id)
|
||
=> GetSetting(id, Setting.SettingType.Bool, s => s.BoolValue);
|
||
|
||
#endregion
|
||
|
||
#region Private Helpers
|
||
private static bool IsInitialized()
|
||
{
|
||
if (currentSchemeDTO == null)
|
||
{
|
||
Debug.LogError("SettingsManager not initialized! Call Initialize() first.");
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
private static SettingDTO FindSetting(string id)
|
||
{
|
||
foreach (var group in currentSchemeDTO.groups)
|
||
{
|
||
if (group?.Settings == null) continue;
|
||
|
||
foreach (var setting in group.Settings)
|
||
{
|
||
if (setting != null && setting.Id == id)
|
||
{
|
||
return setting;
|
||
}
|
||
}
|
||
}
|
||
|
||
Debug.LogWarning($"Setting not found: {id}");
|
||
return null;
|
||
}
|
||
#endregion
|
||
} |