From 29431990f61175280d2dd1588ab5d454b14a47f1 Mon Sep 17 00:00:00 2001 From: AlexCube Date: Sun, 13 Sep 2026 19:54:48 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A5=D1=80=D0=B0=D0=BD=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D1=89=D0=B5=20=D0=B1=D1=80=D0=B0=D1=81=D0=BB=D0=B5=D1=82=D0=B0?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B6=D0=B8=D0=B2=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA?= =?UTF-8?q?;=20=D1=82=D0=BE=D1=80=D0=B3=D0=BE=D0=B2=D1=86=D1=8B=20=D1=87?= =?UTF-8?q?=D1=91=D1=80=D0=BD=D0=BE-=D0=B1=D0=B5=D0=BB=D1=8B=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправляет первый баг-репорт мода на Nexus (youkia96581, 11.09.2026): "Items stored in the space bracelet will disappear after leaving the game and going online again". Причина была записана в коде как нерешённая: PlayerVaults - обычный статический Dictionary, save/load не существовало. ХРАНИЛИЩЕ ТЕПЕРЬ ЖИВЁТ В PlayerDataFile, рядом с рюкзаком игрока. Так решено после вопроса пользователя "почему не сделать принцип как у ящика?": ящик хранит вещи тем, что они лежат в чанке (у TileEntity единственный конструктор TileEntity(Chunk)), а браслету нужен был дом в чём-то, что движок и так сохраняет. Четыре постфикса - FromPlayer/Write/Read/ToPlayer, блоб с магией "NECROVLT" и явной длиной дописывается после всего ванильного. Байтовая часть - в сателлитной сборке: PooledBinaryWriter.Write не резолвится из основного проекта (CS7069), как и у PyramidWardWriteHelper. Два дефекта, найденные и убитые по дороге живыми тестами: 1. ModEvents.WorldShuttingDown приходит ПЕРЕД финальным сохранением игрока (GameManager.SaveAndCleanupWorld: событие на IL_0026, SaveLocalPlayerData на IL_00c4). Обработчик, чистивший там кэш, затирал хранилище на каждом корректном выходе. Обработчик убран; свежесть решает авторитетность ToPlayer, а не таймер. 2. Пустой сессионный кэш трактовался как "хранилища нет" и записывался поверх настоящего. Путь восстановления имеет право не сработать, удалять он права не имеет - добавлена страховка LastLoadedVault. Проверено в игре: положил -> вышел -> запустил заново -> вещи на месте, блоб на 54 байта сверен в .ttp побайтово. ТОРГОВЦЫ (npcTraderJoel/Rekt/Bob/Hugh/Jen) - чёрно-белые. Шейдер НЕ подменяется: материал клонируется со своим шейдером, меняется только текстура альбедо на обесцвеченную копию, так что свет, нормали и скиннинг остаются движковыми. Альбедо ищется обходом свойств шейдера, а не по имени: тело - Game/Character/_Albedo, волосы - Game/Autodesk/_MainTex. Плюс 1% прозрачности с сохранением _ZWrite. Опрос раз в 2 с, потому что торговцы стримятся на подходе, а Джен собирается в рантайме. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FEXvXg1FSAQJHrvYbeAKqq --- HarmonySrc/GhostTraderPatch.cs | 419 +++++++++++++++++++++++++ HarmonySrc/ModEntry.cs | 9 + HarmonySrc/SpatialVaultPatch.cs | 55 +++- HarmonySrc/SpatialVaultPersistence.cs | 366 +++++++++++++++++++++ NecromancerHarmony.dll | Bin 68096 -> 75264 bytes NecromancerHarmony.pdb | Bin 27124 -> 25148 bytes NecromancerTEPersistence.dll | Bin 4608 -> 5632 bytes NecromancerTEPersistence.pdb | Bin 1432 -> 2348 bytes TEPersistenceSrc/SpatialVaultBlobIO.cs | 99 ++++++ 9 files changed, 931 insertions(+), 17 deletions(-) create mode 100644 HarmonySrc/GhostTraderPatch.cs create mode 100644 HarmonySrc/SpatialVaultPersistence.cs create mode 100644 TEPersistenceSrc/SpatialVaultBlobIO.cs diff --git a/HarmonySrc/GhostTraderPatch.cs b/HarmonySrc/GhostTraderPatch.cs new file mode 100644 index 0000000..2b76d53 --- /dev/null +++ b/HarmonySrc/GhostTraderPatch.cs @@ -0,0 +1,419 @@ +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; + +namespace NecromancerTome +{ + /// + /// Renders every trader in black and white (user request 2026-09-13: "сделать модельки всех + /// торговцев полупрозрачными и чёрнобелыми", then after two in-game looks: "прозрачность у + /// торговцев убираем совсем"). Fits the mod - the necromancer deals with the dead, and the + /// only people still trading are not quite alive. + /// + /// TRANSPARENCY WAS DROPPED, THEN ASKED BACK FOR AT A SLIVER. The user first said "убираем + /// совсем", confirmed the result ("торговец стал непрозрачным и полностью чёрно-белым, как и + /// требовалось"), and then asked for "лёгкую прозрачность, буквально 1%" to push him a little + /// further towards a ghost. Dropping it was still the release this effect needed, because it + /// is what allowed the shader to stay put - see below; the 1% is now a separate, optional + /// layer on top (ApplyTransparency) that cannot break the greyscale if the shaders refuse it. + /// + /// The two failed attempts are worth keeping written down, because neither could have been + /// predicted from the decompiler and each was settled by one log line: + /// + /// 1. "Unlit/Transparent Greyscale" (what GameManager uses for greyed-out item icons) has + /// NO _Color - nowhere to put an alpha. Traders came out opaque, and because that shader + /// is built for NGUI atlases rather than skinned meshes, the user saw them "в негативе". + /// 2. "Unlit/Transparent Colored" has no _Color either: `has _Color: False` in the log. + /// NGUI tints through VERTEX colours, not a material property, and a character mesh has + /// none - so that whole family of shaders was always a dead end here. + /// + /// WITHOUT THE ALPHA REQUIREMENT THE SHADER DOES NOT HAVE TO BE REPLACED AT ALL, and that is + /// strictly better than anything above: the material is cloned with its own shader intact and + /// only its albedo texture is swapped for a desaturated copy. Lighting, normal maps, specular, + /// skinning - all still the game's own. The trader looks exactly like himself, in black and + /// white. Nothing can go "negative", because nothing but the pixels changes. + /// + /// THE ALBEDO IS FOUND, NOT ASSUMED - this is what the earlier runs bought us. The first + /// attempt reached for _MainTex and produced an untextured silhouette, because traders are + /// drawn by TWO different shaders and only one of them uses that name. The probe below walks + /// the shader's declared properties instead, and the log then said exactly what they are: + /// + /// shader 'Game/Character' texture properties: _Albedo=set, _Normal=set, _RMOE=set, + /// _texcoord=empty; chosen albedo: HD_Rekt 4096x4096 + /// shader 'Game/Autodesk' texture properties: _MainTex=set, _BumpMap=set, ...; + /// chosen albedo: HD_Rekt_Hair 2048x2048 + /// + /// So the body uses _Albedo and the hair uses _MainTex - which is precisely why the name is + /// discovered rather than hard-coded, and why the probe stays in: Jen is assembled by a + /// different character system than Rekt (AvatarSDCSController vs AvatarNpcController in + /// entityclasses.xml) and may well introduce a third shader. + /// + /// GREYSCALE IS DONE TO THE TEXTURE, via a RenderTexture round trip. The round trip is the + /// point: game textures are compressed with isReadable=false, so GetPixels on the original + /// throws - blitting into an ARGB32 RenderTexture and reading THAT back is the standard way to + /// reach pixels the CPU was never handed. Luma weights 0.299/0.587/0.114 rather than a flat + /// average, so it reads like a black-and-white photograph instead of a muddy one. Cached per + /// source texture: these are 4096x4096, and a readback per renderer per sweep would be + /// indefensible. + /// + /// WHY A TICK AND NOT A SPAWN HOOK. Traders are streamed in on approach ("force spawning + /// pending entity npcTraderRekt" appeared ~4 minutes after the world loaded), and Jen is built + /// at runtime, so her renderers do not all exist when the entity is added to the world. + /// Polling with ModEvents.UnityUpdate - the same approach PetFollowPatch.cs already uses here - + /// avoids guessing at the right moment inside someone else's character pipeline. A trader with + /// no renderers yet is simply not marked done and is picked up on the next sweep. + /// + public static class GhostTraderPatch + { + /// Seconds between sweeps. + public const float SweepInterval = 2f; + + /// 1 = solid. 0.99 is the "буквально 1%" the user asked for on 2026-09-13 after + /// seeing the black-and-white traders: a hint of not-quite-there rather than a ghost. + /// Deliberately close to opaque for a second reason too - see ApplyTransparency, which + /// keeps depth writing on precisely because a nearly-solid character can afford to. + public const float GhostAlpha = 0.99f; + + /// Colour properties that might carry an alpha, best first. + public static readonly string[] TintNameHints = { "_Color", "_BaseColor", "_TintColor", "_Tint" }; + + /// Entity ids already converted. Cleared when the world unloads. + public static readonly HashSet Ghosted = new HashSet(); + + /// Source shader names already described in the log, so the probe says each + /// distinct thing once rather than once per trader per part. + public static readonly HashSet ProbedShaders = new HashSet(); + + /// Desaturated copies, keyed by the texture they came from. + public static readonly Dictionary GreyTextures = new Dictionary(); + + /// Property names that look like an albedo, best first. Confirmed in game: + /// "Game/Character" uses _Albedo, "Game/Autodesk" uses _MainTex. Anything else falls + /// through to "the first texture property that has something in it". + public static readonly string[] AlbedoNameHints = + { + "_MainTex", "_Albedo", "_BaseMap", "_BaseColorMap", "_AlbedoMap", "_DiffuseMap", + "_Diffuse", "_ColorMap", "_MainTexture", "_Texture" + }; + + public static float timer; + + /// Called from ModEntry.InitMod. + public static void Init() + { + ModEvents.UnityUpdate.RegisterHandler(OnUnityUpdate); + ModEvents.WorldShuttingDown.RegisterHandler(OnWorldShuttingDown); + } + + public static void OnWorldShuttingDown(ref ModEvents.SWorldShuttingDownData _data) + { + Ghosted.Clear(); + GreyTextures.Clear(); + timer = 0f; + } + + public static void OnUnityUpdate(ref ModEvents.SUnityUpdateData _data) + { + timer += Time.deltaTime; + if (timer < SweepInterval) + { + return; + } + timer = 0f; + + World world = GameManager.Instance != null ? GameManager.Instance.World : null; + if (world == null || world.EntityAlives == null) + { + return; + } + + for (int i = 0; i < world.EntityAlives.Count; i++) + { + EntityAlive entity = world.EntityAlives[i]; + if (!(entity is EntityTrader trader) || trader.IsDead()) + { + continue; + } + if (Ghosted.Contains(trader.entityId)) + { + continue; + } + if (ApplyGreyscale(trader)) + { + Ghosted.Add(trader.entityId); + } + } + } + + /// False when there is nothing to work on yet (model not built), so the caller + /// leaves this trader unmarked and tries again on the next sweep. + public static bool ApplyGreyscale(EntityTrader _trader) + { + Renderer[] renderers = _trader.GetComponentsInChildren(true); + if (renderers == null || renderers.Length == 0) + { + return false; + } + + int converted = 0; + foreach (Renderer renderer in renderers) + { + if (renderer == null || renderer is ParticleSystemRenderer) + { + continue; + } + + Material[] sources = renderer.sharedMaterials; + if (sources == null || sources.Length == 0) + { + continue; + } + + Material[] greys = new Material[sources.Length]; + bool anyChanged = false; + for (int i = 0; i < sources.Length; i++) + { + greys[i] = MakeGreyMaterial(sources[i], ref anyChanged); + } + if (anyChanged) + { + renderer.materials = greys; + converted++; + } + } + + Debug.Log("[NecromancerTome] GhostTraderPatch: " + _trader.EntityClass.entityClassName + + " (entity " + _trader.entityId + ") - " + converted + " of " + renderers.Length + " renderer(s) desaturated"); + return true; + } + + /// Clone of the source material - SAME shader, same everything - with only its + /// albedo replaced by a black-and-white copy. + public static Material MakeGreyMaterial(Material _source, ref bool _changed) + { + if (_source == null) + { + return null; + } + + ProbeShaderOnce(_source); + + Material grey = new Material(_source); + if (ApplyTransparency(grey)) + { + _changed = true; + } + string albedoProperty = FindAlbedoProperty(_source); + if (albedoProperty == null) + { + // Nothing to desaturate on this material; hand back the clone unchanged rather + // than dropping the renderer's material entirely. + return grey; + } + + Texture2D desaturated = Desaturate(_source.GetTexture(albedoProperty)); + if (desaturated == null) + { + return grey; + } + + grey.SetTexture(albedoProperty, desaturated); + _changed = true; + return grey; + } + + /// + /// Makes the material blend instead of being drawn solid, then dials its alpha down by the + /// requested sliver. Two levers, both conditional, because the traders' own shaders are + /// game-specific and nothing about them can be assumed: + /// + /// - A COLOUR with an alpha channel (_Color and friends). This is the only thing that + /// actually sets the opacity. + /// - THE BLEND MODE (_SrcBlend/_DstBlend). An opaque shader ignores any alpha it is + /// handed, so without this the first lever does nothing visible - the same wall the + /// mod's first transparency attempt hit back on 2026-08-28 with the summoned pets. + /// The recipe is the game's own: MeshDescription.SetupMaterialWithBlendMode writes + /// exactly these properties plus _ZWrite and the _ALPHABLEND_ON keyword. + /// + /// _ZWrite IS LEFT ALONE ON PURPOSE. The usual recipe switches depth writing off, which is + /// right for glass and wrong for a person: without it every part of the model shows through + /// every other part and the trader turns into a soup of overlapping limbs. At 99% opacity + /// there is nothing to see through anyway, so keeping depth writing costs nothing visible + /// and avoids that entirely. + /// + /// Whatever is missing is reported by the probe rather than silently skipped - if neither + /// lever exists on these shaders, the traders stay solid black-and-white and the log says + /// why. + /// + public static bool ApplyTransparency(Material _material) + { + bool touched = false; + + foreach (string hint in TintNameHints) + { + if (!_material.HasProperty(hint)) + { + continue; + } + Color tint = _material.GetColor(hint); + tint.a *= GhostAlpha; + _material.SetColor(hint, tint); + touched = true; + break; + } + + if (_material.HasProperty("_SrcBlend") && _material.HasProperty("_DstBlend")) + { + _material.SetFloat("_SrcBlend", (float)BlendMode.SrcAlpha); + _material.SetFloat("_DstBlend", (float)BlendMode.OneMinusSrcAlpha); + _material.EnableKeyword("_ALPHABLEND_ON"); + _material.renderQueue = (int)RenderQueue.Transparent; + touched = true; + } + + return touched; + } + + /// Name of the texture property holding this material's albedo, or null. Walks + /// the shader's declared properties rather than assuming a name - the body and the hair of + /// the same trader disagree about it. + public static string FindAlbedoProperty(Material _source) + { + Shader shader = _source.shader; + if (shader == null) + { + return null; + } + + foreach (string hint in AlbedoNameHints) + { + if (_source.HasProperty(hint) && _source.GetTexture(hint) != null) + { + return hint; + } + } + + int count = shader.GetPropertyCount(); + for (int i = 0; i < count; i++) + { + if (shader.GetPropertyType(i) != ShaderPropertyType.Texture) + { + continue; + } + string name = shader.GetPropertyName(i); + if (_source.GetTexture(name) != null) + { + return name; + } + } + return null; + } + + /// Black-and-white copy of a texture. See the class comment for why this goes + /// through a RenderTexture instead of reading the source directly. + public static Texture2D Desaturate(Texture _source) + { + if (_source == null) + { + return null; + } + if (GreyTextures.TryGetValue(_source, out Texture2D cached)) + { + return cached; + } + + Texture2D grey = null; + RenderTexture rt = null; + RenderTexture previous = RenderTexture.active; + try + { + rt = RenderTexture.GetTemporary(_source.width, _source.height, 0, + RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); + Graphics.Blit(_source, rt); + RenderTexture.active = rt; + + grey = new Texture2D(_source.width, _source.height, TextureFormat.RGBA32, false); + grey.ReadPixels(new Rect(0f, 0f, _source.width, _source.height), 0, 0); + + Color32[] pixels = grey.GetPixels32(); + for (int i = 0; i < pixels.Length; i++) + { + Color32 p = pixels[i]; + byte luma = (byte)((p.r * 299 + p.g * 587 + p.b * 114) / 1000); + p.r = luma; + p.g = luma; + p.b = luma; + pixels[i] = p; + } + grey.SetPixels32(pixels); + grey.Apply(false, false); + } + catch (System.Exception e) + { + Debug.LogError("[NecromancerTome] GhostTraderPatch: could not desaturate '" + _source.name + "': " + e.Message); + grey = null; + } + finally + { + RenderTexture.active = previous; + if (rt != null) + { + RenderTexture.ReleaseTemporary(rt); + } + } + + // Cached even on failure (as null) so an unreadable texture is not retried per trader. + GreyTextures[_source] = grey; + return grey; + } + + /// Says, once per distinct source shader, which texture properties it has and + /// which carry anything. This is what told us the body uses _Albedo and the hair _MainTex; + /// it stays in because the next trader built by a different character system will announce + /// itself the same way. + public static void ProbeShaderOnce(Material _source) + { + Shader shader = _source.shader; + string shaderName = shader != null ? shader.name : ""; + if (!ProbedShaders.Add(shaderName) || shader == null) + { + return; + } + + // EVERY property, not just the textures. The texture-only version answered the + // "where is the albedo" question; this one has to answer "is there anything here that + // can make it transparent at all", and that lives among the floats and colours. + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + int count = shader.GetPropertyCount(); + for (int i = 0; i < count; i++) + { + string name = shader.GetPropertyName(i); + ShaderPropertyType type = shader.GetPropertyType(i); + sb.Append(sb.Length > 0 ? ", " : "").Append(name).Append(':').Append(type); + if (type == ShaderPropertyType.Texture) + { + sb.Append(_source.GetTexture(name) != null ? "=set" : "=empty"); + } + } + + string chosen = FindAlbedoProperty(_source); + Texture chosenTexture = chosen != null ? _source.GetTexture(chosen) : null; + string tint = ""; + foreach (string hint in TintNameHints) + { + if (_source.HasProperty(hint)) + { + tint = hint + " (alpha " + _source.GetColor(hint).a.ToString("0.###") + ")"; + break; + } + } + bool canBlend = _source.HasProperty("_SrcBlend") && _source.HasProperty("_DstBlend"); + + Debug.Log("[NecromancerTome] GhostTraderPatch: shader '" + shaderName + "' properties: " + + (sb.Length > 0 ? sb.ToString() : "")); + Debug.Log("[NecromancerTome] GhostTraderPatch: shader '" + shaderName + "' - albedo: " + (chosen ?? "") + + " -> " + (chosenTexture != null ? chosenTexture.name + " " + chosenTexture.width + "x" + chosenTexture.height : "") + + "; tint property: " + tint + "; blend-mode properties present: " + canBlend); + } + } +} diff --git a/HarmonySrc/ModEntry.cs b/HarmonySrc/ModEntry.cs index 7456e14..0f5fbe2 100644 --- a/HarmonySrc/ModEntry.cs +++ b/HarmonySrc/ModEntry.cs @@ -21,6 +21,15 @@ namespace NecromancerTome var harmony = new Harmony("necromancertome.harmony"); harmony.PatchAll(Assembly.GetExecutingAssembly()); PetFollowPatch.Init(); + // SpatialVaultPersistence needs NO Init(): it is four Harmony postfixes that PatchAll + // above already attached. It used to register a WorldShuttingDown handler to clear its + // cache - that handler is exactly what wiped the vault on every clean exit, because + // that event fires BEFORE the final player save (GameManager.SaveAndCleanupWorld: + // event at IL_0026, SaveLocalPlayerData at IL_00c4). Freshness is decided by what was + // read instead; see that file. + // Traders rendered as washed-out ghosts (request 2026-09-13). Polls rather than + // hooks a spawn event - see that file for why the SDCS-built trader forces it. + GhostTraderPatch.Init(); // PyramidWardPatch.cs's TEFeaturePyramidWard needs no Init() call - it's discovered // automatically by the engine's own TileEntityCompositeData reflection scan (see that // file's class doc comment), not registered here like PetFollowPatch's UnityUpdate hook. diff --git a/HarmonySrc/SpatialVaultPatch.cs b/HarmonySrc/SpatialVaultPatch.cs index dc65a6d..0f73a28 100644 --- a/HarmonySrc/SpatialVaultPatch.cs +++ b/HarmonySrc/SpatialVaultPatch.cs @@ -30,19 +30,15 @@ namespace NecromancerTome /// this rounds to 0 - deliberately left as-is, not special-cased away, matching the /// Knife's own "0 at 0 kills is a feature, not a bug" precedent - a tooltip explains it /// instead of silently opening a useless empty window. - /// - PERSISTENCE - the one thing NOT fully solved here, flagged rather than silently - /// assumed: the Bag backing each player's vault lives in a plain in-memory - /// Dictionary<int, Bag> in this file (PlayerVaults below), keyed by entityId. This - /// is reliable for as long as the game process keeps running (survives death/respawn/ - /// relogging within one play session, confirmed by how a static field behaves) but has - /// NOT been wired into any save/load system - closing the game entirely and reloading the - /// save later will NOT bring the vault's contents back (no persistence file, no hook into - /// PersistentPlayerData or a world-save event). Building real cross-session persistence - /// (a custom save file + ModEvents.GameSave/Load hooks, or piggybacking on an owned - /// world entity the way the summoned pets do - unconfirmed whether THOSE actually survive - /// a full restart either) is real, separate follow-up work, not attempted here. Treat - /// this like a session-scoped stash until that's built and confirmed - don't rely on it - /// across game restarts yet. + /// - PERSISTENCE - solved 2026-09-13, see SpatialVaultPersistence.cs. It was NOT solved + /// when this item shipped, and that shortfall is exactly what became the mod's first + /// Nexus bug report (youkia96581, 11 Sep 2026: "Items stored in the space bracelet will + /// disappear after leaving the game and going online again"). PlayerVaults below is still + /// the in-memory, entityId-keyed Dictionary it always was, but it is now only the session + /// cache: the durable copy is written into the player's own PlayerDataFile, alongside the + /// backpack, by four postfixes on FromPlayer/ToPlayer/Write/Read. Read that file's comment + /// for why there ("почему не сделать принцип как у ящика?" - because a chest's items live + /// in a chunk, and the bracelet's closest equivalent home is its owner's save data). /// /// REGULAR ATTACK (index 0) - knock back + slow whatever zombie the crosshair is aimed at: /// - Same raycast mechanism HarmonySrc/ThiefLoopPatch.cs already established for @@ -71,8 +67,9 @@ namespace NecromancerTome public const float MaxRange = 50f; public const float ShoveDistance = 6f; - /// See the class-level comment above for exactly what this does and doesn't - /// guarantee - session-scoped only, not yet saved/loaded across game restarts. + /// Session cache only - the durable copy lives on disk, see + /// SpatialVaultPersistence.cs. Cleared on WorldShuttingDown so a different save loaded + /// afterwards cannot inherit this world's vault through a recycled entityId. public static readonly Dictionary PlayerVaults = new Dictionary(); public static bool Prefix(ItemActionData _actionData, bool _bReleased) @@ -118,7 +115,17 @@ namespace NecromancerTome if (!PlayerVaults.TryGetValue(player.entityId, out Bag bag)) { - bag = new Bag(slotCount); + // Normally a restored vault is already here - the ToPlayer postfix puts it in + // when the game applies the save file to the spawning player. LastLoadedVault is + // the safety net for when that chain does not complete: opening the bracelet must + // never be what silently starts an empty vault over a saved one. Only then is a + // genuinely new bag created. + bag = SpatialVaultPersistence.LastLoadedVault ?? new Bag(slotCount); + if (bag == SpatialVaultPersistence.LastLoadedVault) + { + Debug.Log("[NecromancerTome] SpatialVaultPatch: session cache was empty, adopted the last loaded vault (" + + bag.SlotCount + " slots, " + bag.GetUsedSlotCount() + " used)"); + } PlayerVaults[player.entityId] = bag; } else if (bag.SlotCount < slotCount) @@ -134,7 +141,21 @@ namespace NecromancerTome Debug.Log("[NecromancerTome] SpatialVaultPatch: owner=" + player.entityId + " opened vault, " + slotCount + " slots (Necromancy level " + level + ")"); LocalPlayerUI playerUI = LocalPlayerUI.GetUIForPlayer(player); - XUiC_BagStorageWindowGroup.Open(playerUI.xui, player, bag, LootContainer.GetLootContainer("roboticDrone"), Localization.Get("braceletSpatialVaultWindowTitle")); + // The trailing callbacks are vanilla's own optional parameters (_onModified, _onClose). + // _onModified is not needed: the vault lives in PlayerVaults, and PlayerDataFile's + // FromPlayer postfix reads it fresh every time the game saves the player, so there is + // nothing to flush per item move. _onClose asks for a player-data save right away, so + // closing the window is a commit point rather than waiting for the next autosave - + // SaveLocalPlayerData is the game's own routine call and no-ops when saving is not + // active (which is the correct behaviour on a client, where the server owns the file). + XUiC_BagStorageWindowGroup.Open( + playerUI.xui, + player, + bag, + LootContainer.GetLootContainer("roboticDrone"), + Localization.Get("braceletSpatialVaultWindowTitle"), + null, + () => GameManager.Instance.SaveLocalPlayerData()); } public static void ShoveZombieAtCrosshair(EntityPlayerLocal player) diff --git a/HarmonySrc/SpatialVaultPersistence.cs b/HarmonySrc/SpatialVaultPersistence.cs new file mode 100644 index 0000000..ac6b1e6 --- /dev/null +++ b/HarmonySrc/SpatialVaultPersistence.cs @@ -0,0 +1,366 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using HarmonyLib; +using UnityEngine; + +namespace NecromancerTome +{ + /// + /// Cross-restart persistence for the Spatial Bracelet's vault - the fix for the first bug + /// report the mod ever got on Nexus (youkia96581, 11 Sep 2026: "Items stored in the space + /// bracelet will disappear after leaving the game and going online again"). + /// + /// WHY IT LIVES IN THE PLAYER'S SAVE FILE - "почему не сделать принцип как у ящика?" (user, + /// 13.09.2026). Right question, and it decided the design. A chest keeps its items because + /// they live in a TileEntity, and a TileEntity belongs to a CHUNK: decompiled, `TileEntity` + /// has chunkPos and chunk fields and its ONLY constructor is TileEntity(Chunk). The game saves + /// and syncs the chunk; the container rides along. That is the whole trick - not a "storage + /// system" one can call, but a home in something the engine already persists. The bracelet has + /// no position and no chunk, so it got the closest equivalent for something personal: the + /// player's own save data, written right after everything vanilla writes, in the same file and + /// the same moment as the backpack. + /// + /// THAT ALSO ANSWERS THE ID QUESTION ("у браслета, как и у ящика, наверняка есть id"). A + /// chest's id IS its position. An item has no per-instance id by default - ItemValue.type is + /// the item CLASS, identical on every bracelet - but ItemValue.Metadata would hold one and + /// genuinely round-trips through saves (ItemValue.Write writes it, ItemValue.ReadData reads it + /// back; both checked). Per-bracelet vaults are therefore buildable and deliberately not built: + /// keying by the item means losing the bracelet locks the items away forever even though they + /// are still in the save file, and it would let ten bracelets be ten warehouses. Keying by the + /// player - which storing them IN the player's file does for free - has neither problem. + /// + /// THE FOUR HOOKS: + /// FromPlayer - live player -> file object: attach that player's vault to the file. + /// Write - file object -> bytes (Save to disk, or WriteNetwork to the wire, which is + /// literally Write + PlayerMetaInfo): append the vault blob. + /// Read - bytes -> file object: pull the vault back off the stream. + /// ToPlayer - file object -> live player: hand the vault back. + /// FromPlayer always reads the CURRENT vault, so there is no dirty flag and no save scheduling + /// to get wrong: whenever the game saves the player, it saves the vault. + /// + /// ================================================================================ + /// THE BUG THAT COST TWO TEST RUNS, AND WHY IT IS WORTH A BIG COMMENT + /// ================================================================================ + /// Earlier versions cleared the session cache from a ModEvents.WorldShuttingDown handler, to + /// stop one save's vault leaking into the next. The user reported the vault kept losing its + /// contents, and the diagnostics printed the murder weapon in order: + /// + /// INF SaveAndCleanupWorld + /// [NecromancerTome] world shutting down, dropped 1 in-memory vault(s) + /// [NecromancerTome] FromPlayer entity 171 - vault NONE + /// [NecromancerTome] Write - no vault attached (writes an EMPTY marker) + /// + /// **WorldShuttingDown fires BEFORE the final player save, not after.** Confirmed in + /// GameManager.SaveAndCleanupWorld by decompilation rather than inferred from the log: the + /// event is invoked at IL_0026 and SaveLocalPlayerData() is called at IL_00c4, a hundred-odd + /// instructions later. So the handler emptied the cache, and the save that followed + /// faithfully recorded "this player has no vault" over the real one. Every clean exit wiped + /// the vault - which is exactly the symptom the Nexus report described, reintroduced by the + /// fix for it. + /// + /// There is no documentation to have checked first: the community consensus is that the + /// official ModAPI is barebones and has no reference for event ordering, so the decompiler is + /// the only authority. Treat every ModEvent's position in the shutdown sequence as unknown + /// until read out of the method that invokes it. + /// + /// TWO RULES CAME OUT OF IT, and both are load-bearing here: + /// + /// 1. A RESTORE PATH MAY FAIL; IT MAY NEVER DELETE. An empty session cache is not evidence + /// that the player has no vault - it is the absence of evidence. LastLoadedVault below is + /// the safety net, so a broken restore chain costs a restore, not the data. + /// 2. FRESHNESS IS DECIDED BY WHAT WAS READ, NOT BY A TIMER. Cross-save leaking is now + /// prevented by ToPlayer being authoritative: a player file that was read and explicitly + /// carried no vault CLEARS the cache. Nothing has to be cleared "at the right moment" + /// any more, which is what made the old approach fragile in the first place. + /// + public static class SpatialVaultPersistence + { + /// Payload layout version, independent of the blob framing in + /// SpatialVaultBlobIO. An unknown version is skipped, not guessed at - the framing's + /// explicit length means we can always step over a payload we do not understand. + public const byte PayloadVersion = 1; + + /// What a PlayerDataFile carries. A class rather than a bare Bag because its mere + /// PRESENCE is information: "this file has been read/filled, and the answer - including a + /// null Bag - is authoritative". ConditionalWeakTable cannot store null, so a null Bag + /// needs a wrapper to be expressible at all. + public class VaultSlot + { + public Bag Bag; + } + + /// Vault attached to a PlayerDataFile while it is being written, read or + /// converted. Weak, because PlayerDataFile objects are created fresh for every save and + /// every network packet and nothing here should keep one alive. + public static readonly ConditionalWeakTable AttachedVaults = + new ConditionalWeakTable(); + + /// + /// Last vault seen this session, kept outside the weak table. This is rule 1 above made + /// concrete: if the Read -> ToPlayer -> PlayerVaults chain ever fails to complete, the bag + /// is still here, so the next save writes the real contents instead of an empty marker. + /// + /// SINGLE LOCAL PLAYER ONLY. There is one of these per process, so on a dedicated server + /// it would be one player's vault handed to whoever asked next. Every use is gated on the + /// player being an EntityPlayerLocal - which a dedicated server does not have, and a host + /// or single-player game has exactly one of. + /// + public static Bag LastLoadedVault; + + /// Last line printed by the save path, so an unchanged vault saved over and over + /// does not repeat itself in the log. Kept 2026-09-13 when the fix was confirmed: the + /// save pair fires on every autosave, and a player's log should not carry two lines of + /// inventory listing every few minutes - but the moment anything CHANGES it still says so, + /// which is the part that had diagnostic value. + public static string lastSaveLogged; + + /// Builds the opaque payload SpatialVaultBlobIO wraps. Uses netstandard's own + /// BinaryWriter over a MemoryStream, which is why Bag serialization can stay in this + /// project instead of the satellite assembly. + public static byte[] BuildPayload(Bag _bag) + { + using (MemoryStream ms = new MemoryStream()) + using (BinaryWriter bw = new BinaryWriter(ms)) + { + bw.Write(PayloadVersion); + bool hasBag = _bag != null; + bw.Write(hasBag); + if (hasBag) + { + // Plain BinaryWriter is enough: Bag.Write only demands a PooledBinaryWriter + // when bag.preferences != null, and vault bags come from `new Bag(int)`, whose + // constructor sets nothing but the item array. + _bag.Write(bw); + } + bw.Flush(); + return ms.ToArray(); + } + } + + /// Null when the payload holds no vault or is a version we do not know. + public static Bag ParsePayload(byte[] _payload) + { + if (_payload == null || _payload.Length == 0) + { + return null; + } + using (MemoryStream ms = new MemoryStream(_payload, false)) + using (BinaryReader br = new BinaryReader(ms)) + { + byte version = br.ReadByte(); + if (version != PayloadVersion) + { + Debug.LogWarning("[NecromancerTome] SpatialVaultPersistence: vault payload version " + version + ", expected " + PayloadVersion + " - skipped"); + return null; + } + if (!br.ReadBoolean()) + { + return null; + } + // Bag.Read is the STATIC one and returns a new Bag; ReadInto is the instance + // version. Symmetric with BuildPayload: preferences were written as absent, so no + // PooledBinaryReader is needed here either. + return Bag.Read(br); + } + } + + public static void Attach(PlayerDataFile _file, Bag _bag) + { + AttachedVaults.Remove(_file); + AttachedVaults.Add(_file, new VaultSlot { Bag = _bag }); + } + + /// Contents of a bag, for the log. Item names rather than just a count, because + /// "2 slots, 0 used" was true and useless three test runs in a row - what was needed was + /// whether the items the user put in had actually reached this object. + public static string Describe(Bag _bag) + { + if (_bag == null) + { + return "NONE"; + } + ItemStack[] slots = _bag.GetSlots(); + StringBuilder sb = new StringBuilder(); + sb.Append(_bag.SlotCount).Append(" slots, ").Append(_bag.GetUsedSlotCount()).Append(" used"); + if (slots != null) + { + for (int i = 0; i < slots.Length; i++) + { + ItemStack stack = slots[i]; + if (stack == null || stack.IsEmpty()) + { + continue; + } + string name = stack.itemValue != null && stack.itemValue.ItemClass != null + ? stack.itemValue.ItemClass.GetItemName() + : "?"; + sb.Append(" [").Append(i).Append("]=").Append(name).Append("x").Append(stack.count); + } + } + return sb.ToString(); + } + } + + /// Live player -> save file: take the vault along. + [HarmonyPatch(typeof(PlayerDataFile), "FromPlayer")] + public static class Patch_PlayerDataFile_FromPlayer_SpatialVault + { + public static void Postfix(PlayerDataFile __instance, EntityPlayer _player) + { + try + { + if (_player == null) + { + return; + } + Patch_ItemActionEat_ExecuteAction_SpatialVault.PlayerVaults.TryGetValue(_player.entityId, out Bag bag); + string source = bag != null ? "session cache" : null; + if (bag == null && _player is EntityPlayerLocal && SpatialVaultPersistence.LastLoadedVault != null) + { + // Rule 1: never write "no vault" over a vault we know exists. + bag = SpatialVaultPersistence.LastLoadedVault; + source = "last loaded (session cache was empty)"; + } + SpatialVaultPersistence.Attach(__instance, bag); + string line = "FromPlayer entity " + _player.entityId + " - " + SpatialVaultPersistence.Describe(bag) + + (source != null ? ", from " + source : ""); + if (line != SpatialVaultPersistence.lastSaveLogged) + { + SpatialVaultPersistence.lastSaveLogged = line; + Debug.Log("[NecromancerTome] SpatialVaultPersistence: " + line); + } + } + catch (Exception e) + { + Debug.LogError("[NecromancerTome] SpatialVaultPersistence: FromPlayer postfix failed: " + e); + } + } + } + + /// + /// Save file -> live player: hand the vault back. This is also where freshness is decided + /// (rule 2): a file that WAS read and explicitly carried no vault clears the cache, so loading + /// a different save cannot inherit the previous world's vault. Only a file that was never read + /// at all falls back to LastLoadedVault, which is the broken-chain safety net. + /// + [HarmonyPatch(typeof(PlayerDataFile), "ToPlayer")] + public static class Patch_PlayerDataFile_ToPlayer_SpatialVault + { + public static void Postfix(PlayerDataFile __instance, EntityPlayer _player) + { + try + { + if (_player == null) + { + return; + } + bool isLocal = _player is EntityPlayerLocal; + string note; + Bag bag; + + if (SpatialVaultPersistence.AttachedVaults.TryGetValue(__instance, out SpatialVaultPersistence.VaultSlot slot)) + { + bag = slot.Bag; + note = bag != null ? "from this player file" : "this player file says there is no vault"; + } + else if (isLocal && SpatialVaultPersistence.LastLoadedVault != null) + { + bag = SpatialVaultPersistence.LastLoadedVault; + note = "nothing attached to this file - fell back to the last loaded vault"; + } + else + { + bag = null; + note = "nothing attached and nothing loaded"; + } + + if (bag != null) + { + Patch_ItemActionEat_ExecuteAction_SpatialVault.PlayerVaults[_player.entityId] = bag; + } + else + { + Patch_ItemActionEat_ExecuteAction_SpatialVault.PlayerVaults.Remove(_player.entityId); + } + if (isLocal) + { + SpatialVaultPersistence.LastLoadedVault = bag; + } + + Debug.Log("[NecromancerTome] SpatialVaultPersistence: ToPlayer entity " + _player.entityId + + " - " + SpatialVaultPersistence.Describe(bag) + " (" + note + ")"); + } + catch (Exception e) + { + Debug.LogError("[NecromancerTome] SpatialVaultPersistence: ToPlayer postfix failed: " + e); + } + } + } + + /// Appends the vault after everything vanilla wrote - to disk via Save, or to the + /// wire via WriteNetwork. + [HarmonyPatch(typeof(PlayerDataFile), "Write")] + public static class Patch_PlayerDataFile_Write_SpatialVault + { + public static void Postfix(PlayerDataFile __instance, PooledBinaryWriter _bw) + { + try + { + SpatialVaultPersistence.AttachedVaults.TryGetValue(__instance, out SpatialVaultPersistence.VaultSlot slot); + Bag bag = slot != null ? slot.Bag : null; + SpatialVaultBlobIO.Write(_bw, SpatialVaultPersistence.BuildPayload(bag)); + if (bag == null) + { + // Always shouted: writing an empty marker is how the vault got destroyed twice, + // so it must never again scroll past unnoticed. + Debug.LogWarning("[NecromancerTome] SpatialVaultPersistence: Write - no vault attached (writes an EMPTY marker)"); + } + } + catch (Exception e) + { + Debug.LogError("[NecromancerTome] SpatialVaultPersistence: Write postfix failed: " + e); + } + } + } + + /// Reads the vault back off the stream. Must never throw: PlayerDataFile.Load treats + /// any exception out of Read as "this save is broken, fall back to the .bak". + [HarmonyPatch(typeof(PlayerDataFile), "Read")] + public static class Patch_PlayerDataFile_Read_SpatialVault + { + public static void Postfix(PlayerDataFile __instance, PooledBinaryReader _br) + { + try + { + byte[] payload = SpatialVaultBlobIO.TryRead(_br); + if (payload == null) + { + // No vault block: a save from before this feature existed, or player data from + // somebody without the mod. Deliberately NOT recorded as an authoritative + // "no vault" - an absent block is silence, not a denial, and ToPlayer's + // fallback is what should handle it. SpatialVaultBlobIO has already put the + // stream position back. + Debug.Log("[NecromancerTome] SpatialVaultPersistence: Read - no vault block on this stream"); + return; + } + + Bag bag = SpatialVaultPersistence.ParsePayload(payload); + // Attached even when null: a blob that says "no vault" IS an answer, and ToPlayer + // uses it to clear a stale cache when a different save is loaded. + SpatialVaultPersistence.Attach(__instance, bag); + if (bag != null) + { + SpatialVaultPersistence.LastLoadedVault = bag; + } + Debug.Log("[NecromancerTome] SpatialVaultPersistence: Read - blob of " + payload.Length + + " byte(s), " + SpatialVaultPersistence.Describe(bag)); + } + catch (Exception e) + { + Debug.LogError("[NecromancerTome] SpatialVaultPersistence: Read postfix failed: " + e); + } + } + } +} diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 4bd0df65f6878402ea0af8acc921c85ce303a8f3..b0500621d9c450dc828e08ae1a1b2a1b5821cfb7 100644 GIT binary patch literal 75264 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 Czg%ko literal 68096 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%0DoZ~iO<*y3{;}cXT*^6S{D|l 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 J&;LIP{2vaY!7cy* diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb index 4a00c5e3ace6ab5764bf9aac9f098dff6a4a4fb4..7d512a7f0c31bf0aa9e630fc3fcf479cafea3c96 100644 GIT binary patch delta 14913 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 diff --git a/NecromancerTEPersistence.dll b/NecromancerTEPersistence.dll index 8d64336fa69b925e7ecd32e7651f99b71ca9bf8e..2a1a9d31b4465140103a8553af77a2fb88545807 100644 GIT binary patch delta 1952 zcmYjSTTC2P82-+gWp-H>SgwUq=mNb^mePd<3Z+*rX=zKrF2#~+ahDzF;+7$>sMNGz z(`egN1xHOxs%@H%Eob39KZqrP4T z^jM{`%IXtq-P_X`;-h%1v!W!!>mc1u(J2|$r(&(hwQh;B!sUJ%*mhAQQn0HOnC5`O zy5!zCRU#|W0e9u5WG}Vj0&rArm71AByL8xHdJ1!Nvqer*x}a z#{M_z=lP-KEpanKT{Wf>j}}X$D&>I)f*-^Xuwc$T!1Q*nR*7}0_$fQ73Q|G?Ta#2= zE#`}2rKaN-8*jp+Wnvq1=y(8^)bW)#LF~0L$Hst-AKLgUl=nPThWo-GH#LRxP&iHQ z#(k;Q05>?Uj>EEq__vKWiK?l}RcS1Ub9fd9wfZG(f@LX9;yL<$mI>@2*KDlYL7+oy6qU!b!vF@=Lmsv2g<)?UXc9HI8~k~YePxVWJ0#xpEbmGuH= zLn6-dHC!PsTK3!C>oOI(CXhwcIpMnI#yX-GH8wU8r@X9MnL2EI#Kt}w!$cjE#5}}o zeu-rjL7~q^VdH9CMz5^H8(1%!h%<5vv5a^bX;A!4=_3~$=`!|d?VNEJdMF+|K83Z& z#*C3y+3YpGj2t>QU)lb} zotKwKwiS(94wtXr;S<80LMTpgIUU)ZZdY-N!-won%?C+wQ(W}Z?V3OKSZ`t7?K7^$ zi_xJjbgA=nx}DHOcDs~LNcP1k-0a0E*&SA+%P%D?!!^9uBfxs%20x28IhC5~fO>$S zK((#-!wq%y1NFhd!C*~8wAKg?HU+}bXiZ~HBp9g=H`EUr^@EKYqe1JQYjYiN{qC8! zAv(L&LcT*;!T-q$Rn$UL`+zqwcO7tzzH1)qaB7)11sCs3Xa!xLzaK07sP40$teMo| z#nP;QQ&-n61YCG_A%NqQ?eQ$z^v$ZgySw*o`95ReN27+RP~10LnlG38tr2&H^{V>^ zE5oxj{VI>^V!LYfc`nFY&3fP|6Zhtd(}Pkwi49gL(;xC9j0V)99s`sh2KfYeXd4hk zE$vV42vQ~ma+QxKdqz$GS zbfdGC!KnTi3m1mSU>5O*y6KPN3R0#Bet_Z+!J$9Yfnz^)DhLkzc6ua zW#i;R%J)J~(7Sh7EK3_}2CFE_W?bPE8=Lq@*jN?KjBYjfEj0h#V99viGWb|8T^JWlMwabgsC;sCA@hwvrwG2FyN0;Bku z##1JcA&%l%{6chyIm2@`p2HugTe-hqY{^JBQJ?O8ana68?u&be3iXEF?wec4mCD7! z%=#VsO_9o+UZ~{f5=T$XR|<3a#ALp_P$&*$>xWBbOnpEGGCo-Y>F zU0+N3r&}b&d0yyVdX(xfoNSrkMTjHn<3_x3rdp3I=yKP3d=3 tlPQ%AUXdS5buSnZm)EYJ8xe%JfUd2J_`-xbxQ{T~S()UE&k diff --git a/NecromancerTEPersistence.pdb b/NecromancerTEPersistence.pdb index 8a0b8dd7645df19822beb2d98ec62c487757dd29..cd06c4375aef6f1ad65467eb99dbf9c666e8af80 100644 GIT binary patch delta 1430 zcmY*Z3s4hR6n(o{667PHl@OpnKn#IEv_i!;I070lDj&f_G?Wq`L*j%4lZ6@iEg&cg zB6bnMj#8vpfA|BGR;2}p+BzVgT2Tj7PzJ`~S4Tt&(!QtCY2VJ-d(VCEp1p70zFT2_ z{-aViDgpqoG$Jt`8qf~$9Pw-bE@1%hkRL@FG`b`sbU_!45hx~$0{5W*E;mFbT@Pb! zH~^+BI2WYVBQX>3?O-Z{8-D$q#_ob|)5Es${U>1?{>p9Xx1Uz3IlIrfx~k*~V8swF0S0J%6Y@eD6W8*x5Xrs1zls zQEKJNR4s`PNE$9mFj~Z51lLKhd`(bf&RO0hb~}5vw@AxBv~u2%oqGIAro_eYB<|)5 z5${K{XHBCFef&;Vq)Mw&8db|hVUn#;q*9ltG$eY-=}ow|$#~AooWJNS+=a@i>Vg}Q z6<;=MU9m(m9rX7=w=^)4JTGV8yS&F+G&s4l%BAAFuWtz6X4FKt{F&1E!|ugGi@r@) zo|m54lX;xqA{16T`P19yYgTv3elL*epBaKCerre<1iWaLe7U5C<0V`-xZ!SypHm_8 zxZ7i~@2k=N$p=Gem>fD!El9k?L*~LD+;eN*0nx2ybx!KO}$m-~h zjf_v(ITHoVx1LH5rzv{tWJM80c`Ql8NT7fFc-Ylkt*cc2u;$R9!z-TY{F-%{OY3C^ z+MGQ}!?BCgyOXHNLO`FT^mqiz9@_S=60hO(mwylZ)K|*f5M@Pv>%~f~J zF-}!ke(dDB&LXGKn{uwmXSMgi+RO}j@(K3G^DASd6K8rwUEDhfTXb1g>ep+t7)QRM zs=a3K?{u;)FF#r$==Kb$EOi=pq*wI~rzdNM47yW^{=zx#jVbe)e4=`WrGB!ucl#eL z7N`2PvZUjSqS& zhTObAIWf+O*_yHC;!Q^03VIeNF<&}`;lnb#{{!hd2FZ$)Y?cH@N$UB8BgV}ii0A>J z5ptNDE9nC|SQ8$N+&BxD-qo6Hv4kXhJ6g6T=fA{S`>tGcqy+ zFoI+tfRW)BGc!;pBiJUeU0?~286Z{aK)FIjb)fZ2KsLhx{{_vC9fHD6?UU~^ice-? zTBUW&<>|YfZ@7H!ABu^|(>9uLcFwXVS0BnptytxF$u(K)ICFkd7OP8YQfc~RIc6Ky zRSoMJwoHy^R-CNJVyXe;HHsaJl*kBiHL&m*bMvo2dZ=Bz0bs>d& zNt5B^f2<{p#*>TLjJZ-kM(Gyhlon53%T~hb;_4LYzFC7^mQg}0oA)6D!&V07zxhVF qvcLV7->Fm(HQ#u6ax90s7%*HJ7)6;G8AO?wI2gp37&sUhnd$*aKW-iX diff --git a/TEPersistenceSrc/SpatialVaultBlobIO.cs b/TEPersistenceSrc/SpatialVaultBlobIO.cs new file mode 100644 index 0000000..a56c771 --- /dev/null +++ b/TEPersistenceSrc/SpatialVaultBlobIO.cs @@ -0,0 +1,99 @@ +using System.IO; + +namespace NecromancerTome +{ + /// + /// Raw byte-level half of the Spatial Bracelet's vault persistence. Lives in this satellite + /// assembly for exactly the reason PyramidWardWriteHelper.cs documents: PooledBinaryWriter's + /// Write overload set cannot be resolved from the main project at all (CS7069), so anything + /// that actually touches a PooledBinaryWriter/PooledBinaryReader has to be compiled here, + /// against the game's own mscorlib. + /// + /// The split is deliberately drawn so that ONLY primitives cross it: this file knows about + /// byte arrays and stream positions, nothing else. Bag/ItemStack serialization stays in the + /// main project, where `Bag.Write(BinaryWriter)` against netstandard's own BinaryWriter + /// already compiles fine (proven - that is how the vault blob is built). Keeping Bag out of + /// here also keeps UnityEngine out of here, which this project's reference setup (NoStdLib + + /// the game's mscorlib, no UnityEngine at all) cannot tolerate. + /// + /// BLOB LAYOUT, appended after everything vanilla PlayerDataFile.Write produces: + /// + /// int64 Magic "NECROVLT" + /// int32 payloadLength + /// byte[] payload (opaque here; the main project builds and parses it) + /// + /// The magic plus the explicit length is what makes this safe to append to somebody else's + /// format. On read we remember the stream position first: if the magic is not there (an old + /// save written before this feature, or a player-data packet from a party that does not have + /// the mod) the position is put back exactly where it was and the caller is told "no vault" - + /// so whatever the game reads next still reads the right bytes. That matters concretely: + /// PlayerDataFile.ReadNetwork calls Read and then goes on to read PlayerMetaInfo from the + /// same stream, and PlayerDataFile.Load treats ANY exception out of Read as "file is broken, + /// roll back to the .bak". Neither may be disturbed, so nothing here throws. + /// + public static class SpatialVaultBlobIO + { + /// ASCII "NECROVLT" as one int64 - distinctive enough that stray bytes will not + /// be mistaken for our block. + public const long Magic = 0x4E4543524F564C54L; + + /// Magic (8) + length (4). + public const int HeaderSize = 12; + + public static void Write(PooledBinaryWriter _bw, byte[] _payload) + { + if (_bw == null || _payload == null) + { + return; + } + _bw.Write(Magic); + _bw.Write(_payload.Length); + _bw.Write(_payload); + } + + /// Returns the payload, or null when this stream carries no vault block. Never + /// throws, and never leaves the stream anywhere the caller did not expect: either just + /// past our whole block, or exactly back where it started. + public static byte[] TryRead(PooledBinaryReader _br) + { + if (_br == null) + { + return null; + } + + Stream stream = _br.BaseStream; + if (stream == null || !stream.CanSeek) + { + return null; + } + + long startPosition = stream.Position; + try + { + if (stream.Length - startPosition < HeaderSize) + { + return null; + } + if (_br.ReadInt64() != Magic) + { + stream.Position = startPosition; + return null; + } + + int length = _br.ReadInt32(); + if (length < 0 || stream.Length - stream.Position < length) + { + stream.Position = startPosition; + return null; + } + + return _br.ReadBytes(length); + } + catch + { + stream.Position = startPosition; + return null; + } + } + } +}