Files
necromants-tome-7d2d-3-2/HarmonySrc/GameVersionGuard.cs
T
AlexCubeandClaude Opus 5.5 f4470b784f 1.4.2: проверка версии игры и хранилище, которое не стирается
Повод: 1.4.1 на игре 3.3.0 experimental не смогла прочитать Пространственное
хранилище (Bag переписан) и при сохранении записала пустой маркер - вещи
пропали навсегда.

GameVersionGuard: версия из Constants.cVersionInformation (не из const
cVersionMinor - та вшивается в DLL), поддерживается Major 3 и Minor/10 == 2,
то есть любая 3.2.x. На другой версии в главном меню окно «мод несовместим,
обновите» с единственной кнопкой «Выйти из игры» (решение пользователя), а
вместо PatchAll ставятся по отдельности только окно и четыре патча
сохранения хранилища. Всё, чем пользуется защита, сверено с декомпиляцией 3.3.

Хранилище: непрочитанные байты (чужая версия или упавший разбор) проходят
Read -> ToPlayer -> FromPlayer -> Write нетронутыми, ключ - entityId, сброс
при открытии главного меню (последнее сохранение игрока делается раньше,
внутри SaveAndCleanupWorld - проверено по коду игры). Пока байты не
прочитаны, хранилище не открывается и не пополняется, с подсказкой.

Проверено в игре на 3.2 через отладочные файлы FORCE_VAULT_UNREAD и
FORCE_VERSION_GUARD (в .gitignore, в архив не попадают): 603 байта
хранилища прошли сохранение и прочитались обратно совпадающими с исходными
(26 ячеек, 24 предмета); окно появляется, Esc его не закрывает, кнопка
закрывает игру. На настоящей 3.3 не проверено.

Localization: 4 новых ключа на 13 языках, всего 165; README - статус и
число ключей (было 149).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 00:09:01 +03:00

152 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.IO;
using HarmonyLib;
using UnityEngine;
namespace NecromancerTome
{
/// <summary>
/// ПРОВЕРКА ВЕРСИИ ИГРЫ, 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, мод ведёт себя так, будто
/// версия несовместима. В релизный архив он не попадает - состав архива задан явным списком.
/// </summary>
public static class GameVersionGuard
{
/// <summary>Под какую версию собран мод: Major и десяток Minor (3.2.x = 3 и 2).</summary>
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");
}
/// <summary>Отладка сохранения хранилища байт в байт на 3.2: при файле FORCE_VAULT_UNREAD
/// в папке мода хранилище из сейва не разбирается, а проходит всю цепочку сохранения
/// нетронутым - ровно как на несовместимой версии, но без окна с выходом, так что в мир
/// можно зайти и сохраниться.</summary>
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");
}
}
/// <summary>Главное меню: здесь показывается окно, и здесь же сбрасываются непрочитанные байты
/// хранилища - возврат в меню значит, что прежний мир закрыт, и его данные не должны перейти к
/// персонажу следующего.</summary>
[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);
}
}
}
}