Сборка под игру 3.3: хранилище, Пирамида, проверка версии
- Пирамида: StreamModeWrite/Read теперь глобальные перечисления (в 3.2 были внутри TileEntity). - Хранилище: Bag поверх ItemStackGrid (конструктор с местом хранения, Resize, ItemGrid), запись и чтение через PooledBinaryWriter/Reader. Формат хранилища 2; формат 1 из сейвов 3.2 читается. Незнакомый номер формата больше не считается «хранилища нет» - байты сохраняются. - Сфера на браслете: ModificationCount/GetModification/SetModification. - Непустой сундук: TEFeatureStorage.IsEmpty (ITileEntityLootable в 3.3 нет). - Поиск ножа в инвентаре через ItemGrid. - GameVersionGuard пускает только 3.3. Проверено в игре на V 3.3.0 b14: хранилище из сейва 3.2 (24 стопки) прочиталось, сохранилось и перечиталось; Пирамида включается; «Жертва», обыск трупа и мешки работают. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f4470b784f
commit
e8e21d5a58
@@ -79,8 +79,16 @@ namespace NecromancerTome
|
||||
{
|
||||
/// <summary>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.</summary>
|
||||
public const byte PayloadVersion = 1;
|
||||
/// explicit length means we can always step over a payload we do not understand.
|
||||
///
|
||||
/// 2 = игра 3.3 (мод 1.5.0): сумка пишется новым Bag.Write (свой байт версии 2 и
|
||||
/// ItemStackGrid). 1 = игра 3.2, старый формат сумки; 3.3 читает его сам (Bag.readInto,
|
||||
/// ветка версии < 2), поэтому хранилище из сейва 3.2 переезжает без потерь.
|
||||
/// Номер поднят, чтобы сборка под 3.2 не приняла новый формат за свой.</summary>
|
||||
public const byte PayloadVersion = 2;
|
||||
|
||||
/// <summary>Старейший номер, который эта сборка ещё читает.</summary>
|
||||
public const byte OldestReadablePayloadVersion = 1;
|
||||
|
||||
/// <summary>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
|
||||
@@ -147,30 +155,34 @@ namespace NecromancerTome
|
||||
/// which is the part that had diagnostic value.</summary>
|
||||
public static string lastSaveLogged;
|
||||
|
||||
/// <summary>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.</summary>
|
||||
/// <summary>Builds the opaque payload SpatialVaultBlobIO wraps. Заголовок (версия и
|
||||
/// «есть ли сумка») пишется в MemoryStream напрямую, а сама сумка - через
|
||||
/// PooledBinaryWriter поверх того же потока: в 3.3 Bag.Write принимает только его.
|
||||
/// Сами Write-перегрузки PooledBinaryWriter из этого проекта не вызываются (см.
|
||||
/// NecromancerTEPersistence.csproj), поэтому заголовок и не идёт через него.</summary>
|
||||
public static byte[] BuildPayload(Bag _bag)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write(PayloadVersion);
|
||||
ms.WriteByte(PayloadVersion);
|
||||
bool hasBag = _bag != null;
|
||||
bw.Write(hasBag);
|
||||
ms.WriteByte(hasBag ? (byte)1 : (byte)0);
|
||||
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);
|
||||
using (PooledBinaryWriter pw = MemoryPools.poolBinaryWriter.AllocSync(_bReset: false))
|
||||
{
|
||||
pw.SetBaseStream(ms);
|
||||
_bag.Write(pw, StreamModeWrite.Persistency);
|
||||
pw.Flush();
|
||||
}
|
||||
}
|
||||
bw.Flush();
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Null when the payload holds no vault or is a version we do not know.</summary>
|
||||
/// <summary>Null when the payload holds no vault. Незнакомый номер версии - исключение,
|
||||
/// а НЕ null: null значит «хранилища нет», и следующее сохранение стёрло бы вещи.
|
||||
/// Исключение ловит Read-постфикс и сохраняет байты нетронутыми (AttachRaw).</summary>
|
||||
public static Bag ParsePayload(byte[] _payload)
|
||||
{
|
||||
if (_payload == null || _payload.Length == 0)
|
||||
@@ -178,22 +190,25 @@ namespace NecromancerTome
|
||||
return null;
|
||||
}
|
||||
using (MemoryStream ms = new MemoryStream(_payload, false))
|
||||
using (BinaryReader br = new BinaryReader(ms))
|
||||
{
|
||||
byte version = br.ReadByte();
|
||||
if (version != PayloadVersion)
|
||||
int version = ms.ReadByte();
|
||||
if (version < OldestReadablePayloadVersion || version > PayloadVersion)
|
||||
{
|
||||
Debug.LogWarning("[NecromancerTome] SpatialVaultPersistence: vault payload version " + version + ", expected " + PayloadVersion + " - skipped");
|
||||
return null;
|
||||
throw new InvalidDataException("vault payload version " + version + ", this build reads " +
|
||||
OldestReadablePayloadVersion + ".." + PayloadVersion);
|
||||
}
|
||||
if (!br.ReadBoolean())
|
||||
if (ms.ReadByte() != 1)
|
||||
{
|
||||
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);
|
||||
// Сумка сама пишет свой номер формата, и Bag.readInto 3.3 понимает оба: и старый
|
||||
// (из сейва 3.2), и новый. Место хранения - как у сумки дрона (то же окно), владельца нет:
|
||||
// так же игра сама создаёт сумку из старого сейва (PlayerDataFile, legacyBag).
|
||||
using (PooledBinaryReader pr = MemoryPools.poolBinaryReader.AllocSync(_bReset: false))
|
||||
{
|
||||
pr.SetBaseStream(ms);
|
||||
return Bag.Read(pr, XUiC_ItemStack.StackLocationTypes.LootContainer, StreamModeRead.Persistency, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,14 +227,14 @@ namespace NecromancerTome
|
||||
{
|
||||
return "NONE";
|
||||
}
|
||||
ItemStack[] slots = _bag.GetSlots();
|
||||
ItemStackGrid slots = _bag.ItemGrid;
|
||||
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];
|
||||
ItemStack stack = slots.GetItem(i);
|
||||
if (stack == null || stack.IsEmpty())
|
||||
{
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user