using System;
using System.IO;
using HarmonyLib;
using UnityEngine;
namespace NecromancerTome
{
///
/// ПРОВЕРКА ВЕРСИИ ИГРЫ, 1.4.2 (2026-09-24). Повод: 1.4.1 на игре 3.3.0 experimental молча
/// потеряла содержимое Пространственного хранилища - разбор в BACKLOG.md, «Игра 3.3.0
/// experimental». Решение пользователя: при несовместимой версии мод показывает в главном меню
/// окно «мод несовместим, обновите» с ЕДИНСТВЕННОЙ кнопкой «Выйти из игры» - без «играть всё
/// равно».
///
/// ПОЧЕМУ ЧИТАЕТСЯ cVersionInformation, А НЕ cVersionMinor. cVersionMinor - const, компилятор
/// вписывает её значение (20) прямо в нашу DLL, и на 3.3 она всё равно «видела» бы 20.
/// cVersionInformation - static readonly, её значение берётся из игры при запуске.
///
/// ПОЧЕМУ Minor / 10. Игра хранит версию двузначным числом: 3.2.0 = 20, 3.2.1 = 21, 3.3.0 = 30
/// (VersionInformation печатает его как {Minor/10}.{Minor%10}). Проверка по десяткам пропускает
/// любые хотфиксы 3.2.x и не пропускает 3.3 и всё, что будет после.
///
/// ВСЁ, ЧЕМ ПОЛЬЗУЕТСЯ ЭТОТ ФАЙЛ, СВЕРЕНО С ДЕКОМПИЛЯЦИЕЙ 3.3.0 (build 25485021): Constants,
/// VersionInformation, XUiC_MainMenu.OnOpen, XUiC_MessageBoxWindowGroup (класс совпадает целиком),
/// Mod.VersionString. Иначе сборка под 3.2 упала бы на 3.3 раньше, чем показала окно.
///
/// ОТЛАДКА НА 3.2: если в папке мода лежит файл FORCE_VERSION_GUARD, мод ведёт себя так, будто
/// версия несовместима. В релизный архив он не попадает - состав архива задан явным списком.
///
public static class GameVersionGuard
{
/// Под какую версию собран мод: Major и десяток Minor (3.2.x = 3 и 2).
public const int SupportedMajor = 3;
public const int SupportedMinorTens = 2;
public const string SupportedText = "3.2";
private static bool? supported;
public static bool IsSupported
{
get
{
if (supported == null)
{
supported = Check();
}
return supported.Value;
}
}
public static string GameVersionText
{
get
{
VersionInformation v = Constants.cVersionInformation;
return v != null ? v.LongStringNoBuild : "?";
}
}
private static bool Check()
{
if (ForcedByFile())
{
Debug.LogWarning("[NecromancerTome] GameVersionGuard: FORCE_VERSION_GUARD file present - acting as if the game version were unsupported");
return false;
}
VersionInformation v = Constants.cVersionInformation;
bool ok = v != null && v.Major == SupportedMajor && v.Minor / 10 == SupportedMinorTens;
Debug.Log("[NecromancerTome] GameVersionGuard: game " + GameVersionText + " (Major " + (v != null ? v.Major : -1) +
", Minor " + (v != null ? v.Minor : -1) + "), mod built for " + SupportedText + " -> " +
(ok ? "supported" : "NOT SUPPORTED"));
return ok;
}
private static bool ForcedByFile()
{
return DebugFileExists("FORCE_VERSION_GUARD");
}
/// Отладка сохранения хранилища байт в байт на 3.2: при файле FORCE_VAULT_UNREAD
/// в папке мода хранилище из сейва не разбирается, а проходит всю цепочку сохранения
/// нетронутым - ровно как на несовместимой версии, но без окна с выходом, так что в мир
/// можно зайти и сохраниться.
public static bool ForceVaultUnread
{
get { return DebugFileExists("FORCE_VAULT_UNREAD"); }
}
private static bool DebugFileExists(string _name)
{
try
{
string folder = ModEntry.Instance != null ? ModEntry.Instance.Path : null;
return folder != null && File.Exists(Path.Combine(folder, _name));
}
catch (Exception)
{
return false;
}
}
public static void ShowIncompatibleWindow(XUi _xui)
{
string modVersion = ModEntry.Instance != null ? ModEntry.Instance.VersionString : "?";
string text = string.Format(Localization.Get("necroVersionGuardText"), modVersion, SupportedText, GameVersionText);
XUiC_MessageBoxWindowGroup.ShowCustom(_xui,
Localization.Get("necroVersionGuardTitle"),
text,
"",
delegate(XUiC_MessageBoxWindowGroup _box)
{
_box.buttons[0].DefaultConfirm("necroVersionGuardQuit", delegate
{
Debug.Log("[NecromancerTome] GameVersionGuard: player pressed Quit");
Application.Quit();
});
},
_openMainMenuOnClose: false,
_modal: true,
_buttonOnOutsideClick: -1,
// Проверено в игре 2026-09-25: Esc модальное окно не закрывает - оно остаётся на
// экране, и уйти можно только кнопкой. Это и нужно. _buttonOnExternalClose: 0 - на
// случай, если игра всё же закроет окно извне: тогда это тоже выход.
_buttonOnExternalClose: 0);
Debug.LogWarning("[NecromancerTome] GameVersionGuard: incompatible game version, quit window shown");
}
}
/// Главное меню: здесь показывается окно, и здесь же сбрасываются непрочитанные байты
/// хранилища - возврат в меню значит, что прежний мир закрыт, и его данные не должны перейти к
/// персонажу следующего.
[HarmonyPatch(typeof(XUiC_MainMenu), "OnOpen")]
public static class Patch_XUiC_MainMenu_OnOpen_VersionGuard
{
public static void Postfix(XUiC_MainMenu __instance)
{
try
{
SpatialVaultPersistence.UnreadableByEntity.Clear();
if (!GameVersionGuard.IsSupported)
{
GameVersionGuard.ShowIncompatibleWindow(__instance.xui);
}
}
catch (Exception e)
{
Debug.LogError("[NecromancerTome] GameVersionGuard: main menu postfix failed: " + e);
}
}
}
}