diff --git a/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs b/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs
new file mode 100644
index 000000000..7889c57c8
--- /dev/null
+++ b/Projects/Server.Tests/Tests/Serialization/AnchoredItemSerializationTests.cs
@@ -0,0 +1,76 @@
+using System;
+using Xunit;
+
+namespace Server.Tests;
+
+[Collection("Sequential Server Tests")]
+public class AnchoredItemSerializationTests
+{
+ private static byte[] SerializeItem(Item item)
+ {
+ var writer = new BufferWriter(new byte[256], true);
+ item.Serialize(writer);
+ return writer.Buffer[..(int)writer.Position];
+ }
+
+ ///
+ /// Item v11 stores LastMoved and DecayResetTime as anchored time: the serialized bytes
+ /// are a function of item state only, not of when the save runs. Pre-v11 stored
+ /// minutes-since-moved and delta time, which rewrote the bytes on every save.
+ ///
+ [Fact]
+ public void ItemBytes_AreStable_AcrossSavesAtDifferentTimes()
+ {
+ var start = Core._now;
+
+ try
+ {
+ var item = new Item(0x1F13);
+ item.MoveToWorld(new Point3D(120, 100, 0), Map.Felucca);
+ item.RestartDecay();
+
+ var first = SerializeItem(item);
+
+ // A save hours later, with no state change, must produce identical bytes.
+ Core._now = start + TimeSpan.FromHours(5);
+ var second = SerializeItem(item);
+
+ Assert.Equal(first, second);
+
+ item.Delete();
+ }
+ finally
+ {
+ Core._now = start;
+ }
+ }
+
+ ///
+ /// Pre-v11 LastMoved was stored at whole-minute precision relative to the save time and
+ /// could never round-trip exactly. Anchored storage is absolute and exact.
+ ///
+ [Fact]
+ public void LastMovedAndDecayReset_RoundTripExactly()
+ {
+ var item = new Item(0x1F13);
+ item.MoveToWorld(new Point3D(121, 100, 0), Map.Felucca);
+
+ // Sub-minute precision that the old minutes encoding would have destroyed.
+ var moved = Core.Now - TimeSpan.FromSeconds(90.5) - TimeSpan.FromMilliseconds(123);
+ item.LastMoved = moved;
+
+ item.RestartDecay();
+ var decayReset = item.DecayResetTime;
+ Assert.NotEqual(default(DateTime), decayReset);
+
+ var bytes = SerializeItem(item);
+
+ var restored = new Item((Serial)0x7ffff123u);
+ restored.Deserialize(new BufferReader(bytes));
+
+ Assert.Equal(moved, restored.LastMoved);
+ Assert.Equal(decayReset, restored.DecayResetTime);
+
+ item.Delete();
+ }
+}
diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs
index a2b63a821..13f249f2f 100644
--- a/Projects/Server/Items/Item.cs
+++ b/Projects/Server/Items/Item.cs
@@ -863,7 +863,7 @@ public partial class Item : IHued, IComparable- , ISpawnable, IObjectPropert
public virtual void Serialize(IGenericWriter writer)
{
- writer.Write(10); // version
+ writer.Write(11); // version
var flags = SaveFlag.None;
@@ -1015,19 +1015,13 @@ public partial class Item : IHued, IComparable
- , ISpawnable, IObjectPropert
writer.Write((int)flags);
- /* begin last moved time optimization */
- var ticks = LastMoved.Ticks;
- var now = Core.Now.Ticks;
-
- var minutes = new TimeSpan(now - ticks).TotalMinutes;
-
- writer.WriteEncodedInt((int)Math.Clamp(minutes, int.MinValue, int.MaxValue));
- /* end */
+ // Anchored: shifted by downtime at load, so time-since-moved is preserved and the
+ // bytes are stable across saves while the item does not move.
+ writer.WriteAnchoredTime(LastMoved);
if (GetSaveFlag(flags, SaveFlag.DecayReset))
{
- //TODO Use WriteAnchoredTime once the save-time anchor is ported
- writer.WriteDeltaTime(info.m_DecayReset);
+ writer.WriteAnchoredTime(info.m_DecayReset);
}
if (GetSaveFlag(flags, SaveFlag.Direction))
@@ -2772,6 +2766,7 @@ public partial class Item : IHued, IComparable
- , ISpawnable, IObjectPropert
switch (version)
{
+ case 11:
case 10:
case 9:
case 8:
@@ -2780,7 +2775,11 @@ public partial class Item : IHued, IComparable
- , ISpawnable, IObjectPropert
{
var flags = (SaveFlag)reader.ReadInt();
- if (version < 7)
+ if (version >= 11)
+ {
+ LastMoved = reader.ReadAnchoredTime();
+ }
+ else if (version < 7)
{
LastMoved = reader.ReadDeltaTime();
}
@@ -2800,10 +2799,10 @@ public partial class Item : IHued, IComparable
- , ISpawnable, IObjectPropert
if (version >= 10 && GetSaveFlag(flags, SaveFlag.DecayReset))
{
- var reset = reader.ReadDeltaTime();
+ var reset = version >= 11 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime();
- // LastMoved is stored at whole-minute precision; keep the stamp only
- // while it still extends the deadline.
+ // Pre-v11 LastMoved was stored at whole-minute precision; keep the
+ // stamp only while it still extends the deadline.
if (reset > LastMoved)
{
DecayResetTime = reset;
diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs
index 0e88449e3..fb028b9d7 100644
--- a/Projects/Server/Mobiles/Mobile.cs
+++ b/Projects/Server/Mobiles/Mobile.cs
@@ -2324,11 +2324,11 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro
public virtual void Serialize(IGenericWriter writer)
{
- writer.Write(37); // version
+ writer.Write(38); // version
- writer.WriteDeltaTime(LastStrGain);
- writer.WriteDeltaTime(LastIntGain);
- writer.WriteDeltaTime(LastDexGain);
+ writer.WriteAnchoredTime(LastStrGain);
+ writer.WriteAnchoredTime(LastIntGain);
+ writer.WriteAnchoredTime(LastDexGain);
byte hairflag = 0x00;
@@ -6150,6 +6150,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro
switch (version)
{
+ case 38: // Stat-gain stamps moved from delta time to anchored time
case 37: // Decomposed hair into inline item id/hue (dropped the VirtualHairInfo object)
case 36: // Moved virtues to VirtueSystem
case 35: // Moved short term murders to PlayerMurderSystem
@@ -6158,9 +6159,18 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro
case 32: // Removed StuckMenu
case 31:
{
- LastStrGain = reader.ReadDeltaTime();
- LastIntGain = reader.ReadDeltaTime();
- LastDexGain = reader.ReadDeltaTime();
+ if (version >= 38)
+ {
+ LastStrGain = reader.ReadAnchoredTime();
+ LastIntGain = reader.ReadAnchoredTime();
+ LastDexGain = reader.ReadAnchoredTime();
+ }
+ else
+ {
+ LastStrGain = reader.ReadDeltaTime();
+ LastIntGain = reader.ReadDeltaTime();
+ LastDexGain = reader.ReadDeltaTime();
+ }
goto case 30;
}
diff --git a/Projects/Server/Serialization/GenericEntityPersistence.cs b/Projects/Server/Serialization/GenericEntityPersistence.cs
index f27b22740..c7323329f 100644
--- a/Projects/Server/Serialization/GenericEntityPersistence.cs
+++ b/Projects/Server/Serialization/GenericEntityPersistence.cs
@@ -504,6 +504,10 @@ public class GenericEntityPersistence : GenericPersistence, IGenericEntityPer
var anchor = new DateTime(dataReader.ReadLong(), DateTimeKind.Utc);
var shift = Core.Now - anchor;
_anchoredTimeShift = anchor.Ticks > 0 && shift > TimeSpan.Zero ? shift : TimeSpan.Zero;
+
+ // The whole save shares one anchor. Publish it so payloads without their own
+ // (GenericPersistence bins) can shift too; indexes load before any of them.
+ World.LoadTimeShift = _anchoredTimeShift;
}
if (version >= 4)
diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs
index 5b8e79c29..2268a86c6 100644
--- a/Projects/Server/Serialization/GenericPersistence.cs
+++ b/Projects/Server/Serialization/GenericPersistence.cs
@@ -98,7 +98,13 @@ public abstract class GenericPersistence : Persistence, IGenericSerializable
byte* ptr = null;
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
- var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb);
+ var dataReader = new UnmanagedDataReader(ptr, accessor.Length, typesDb)
+ {
+ // These payloads carry no anchor of their own; they inherit the save-wide
+ // shift stamped while the entity indexes were read (indexes always load
+ // before persistence payloads — see Persistence.Load).
+ AnchoredTimeShift = World.LoadTimeShift
+ };
Deserialize(dataReader);
error = dataReader.Position != fileLength
diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs
index 4111ef0d1..c00fc85f6 100644
--- a/Projects/Server/World/World.cs
+++ b/Projects/Server/World/World.cs
@@ -99,6 +99,15 @@ public static class World
/// anchored timestamps can be re-based by the downtime at load.
///
public static DateTime SaveStartTime { get; internal set; }
+
+ ///
+ /// The anchored-time shift for the save currently being loaded: the downtime between the
+ /// save's start and this load. Stamped while entity indexes are read (they all carry the
+ /// same anchor, since the whole save shares one ) and applied
+ /// to every reader of that save's files — including
+ /// payloads, which carry no anchor of their own. Zero for saves that predate the anchor.
+ ///
+ public static TimeSpan LoadTimeShift { get; internal set; }
public static bool Running => WorldState is not WorldState.Loading and not WorldState.Initial;
public static bool Loading => WorldState == WorldState.Loading;
diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs
index d1e14c834..9daea0ba3 100755
--- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs
+++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs
@@ -27,16 +27,44 @@ using Server.Logging;
namespace Server.Engines.CannedEvil;
-[SerializationGenerator(10, false)]
+[SerializationGenerator(11, false)]
public partial class ChampionSpawn : Item
{
+ private void MigrateFrom(V10Content content)
+ {
+ _level = content.Level;
+ _activatedByProximity = content.ActivatedByProximity;
+ _nextProximityTime = content.NextProximityTime;
+ _maxLevel = content.MaxLevel;
+ _activatedByValor = content.ActivatedByValor;
+ _damageEntries = content.DamageEntries;
+ _confinedRoaming = content.ConfinedRoaming;
+ _idol = content.Idol;
+ _hasBeenAdvanced = content.HasBeenAdvanced;
+ _spawnArea = content.SpawnArea;
+ _randomizeType = content.RandomizeType;
+ _kills = content.Kills;
+ _active = content.Active;
+ _type = content.Type;
+ _creatures = content.Creatures;
+ _redSkulls = content.RedSkulls;
+ _whiteSkulls = content.WhiteSkulls;
+ _platform = content.Platform;
+ _altar = content.Altar;
+ _expireDelay = content.ExpireDelay;
+ _expireTime = content.ExpireTime;
+ _champion = content.Champion;
+ _restartDelay = content.RestartDelay;
+ _restartTime = content.RestartTime;
+ }
+
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ChampionSpawn));
[SerializableField(1)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private bool _activatedByProximity;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(2)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _nextProximityTime;
@@ -96,7 +124,7 @@ public partial class ChampionSpawn : Item
[SerializedCommandProperty(AccessLevel.GameMaster)]
private TimeSpan _expireDelay;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(20)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _expireTime;
@@ -109,7 +137,7 @@ public partial class ChampionSpawn : Item
[SerializedCommandProperty(AccessLevel.GameMaster)]
private TimeSpan _restartDelay;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(23, setter: "private")]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _restartTime;
diff --git a/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs b/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs
index a5426e7a5..88acd587e 100644
--- a/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs
+++ b/Projects/UOContent/Engines/CannedEvil/StarRoomGate.cs
@@ -3,15 +3,21 @@ using ModernUO.Serialization;
namespace Server.Items;
-[SerializationGenerator(1, false)]
+[SerializationGenerator(2, false)]
public partial class StarRoomGate : Moongate
{
+ private void MigrateFrom(V1Content content)
+ {
+ _decays = content.Decays;
+ _decayTime = content.DecayTime;
+ }
+
private static TimeSpan GateDuration = TimeSpan.FromMinutes(2.0);
[SerializableField(0)]
private bool _decays;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1)]
private DateTime _decayTime;
diff --git a/Projects/UOContent/Engines/Ethics/Core/Player.cs b/Projects/UOContent/Engines/Ethics/Core/Player.cs
index f21fe9eb9..d8aad9eb3 100644
--- a/Projects/UOContent/Engines/Ethics/Core/Player.cs
+++ b/Projects/UOContent/Engines/Ethics/Core/Player.cs
@@ -5,9 +5,20 @@ using Server.Mobiles;
namespace Server.Ethics;
[PropertyObject]
-[SerializationGenerator(1)]
+[SerializationGenerator(2)]
public partial class Player : EthicsEntity
{
+ private void MigrateFrom(V1Content content)
+ {
+ _mobile = content.Mobile;
+ _power = content.Power;
+ _history = content.History;
+ _steed = content.Steed;
+ _familiar = content.Familiar;
+ _shield = content.Shield;
+ _ethic = content.Ethic;
+ }
+
[SerializableField(0, setter: "private")]
private Mobile _mobile;
@@ -27,7 +38,7 @@ public partial class Player : EthicsEntity
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
private Mobile _familiar;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(5, setter: "private")]
private DateTime _shield;
diff --git a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs
index fbb3af7b6..53932c8f0 100644
--- a/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs
+++ b/Projects/UOContent/Engines/Khaldun/PuzzleChest.cs
@@ -162,10 +162,15 @@ namespace Server.Items
}
}
- [SerializationGenerator(0)]
+ [SerializationGenerator(1)]
public partial class PuzzleChestSolutionAndTime : PuzzleChestSolution
{
- [DeltaDateTime]
+ private void MigrateFrom(V0Content content)
+ {
+ _when = content.When;
+ }
+
+ [AnchoredDateTime]
[SerializableField(0)]
private DateTime _when;
diff --git a/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs b/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs
index 5d846d595..d3e39c09f 100644
--- a/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs
+++ b/Projects/UOContent/Engines/ML Quests/MLQuestPersistence.cs
@@ -19,7 +19,7 @@ namespace Server.Engines.MLQuests
{
base.Serialize(writer);
- writer.Write(2); // version
+ writer.Write(3); // version
writer.Write(MLQuestSystem.Contexts.Count);
foreach (var context in MLQuestSystem.Contexts.Values)
diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs
index 2838c7736..77c3350f5 100644
--- a/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs
+++ b/Projects/UOContent/Engines/ML Quests/Objectives/BaseObjective.cs
@@ -119,7 +119,7 @@ namespace Server.Engines.MLQuests.Objectives
if (IsTimed)
{
writer.Write(true);
- writer.WriteDeltaTime(EndTime);
+ writer.WriteAnchoredTime(EndTime);
}
else
{
@@ -135,7 +135,7 @@ namespace Server.Engines.MLQuests.Objectives
{
if (reader.ReadBool())
{
- var endTime = reader.ReadDeltaTime();
+ var endTime = version >= 3 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime();
if (objInstance != null)
{
diff --git a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs
index 396b87dcd..4cd8d8c63 100644
--- a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs
+++ b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs
@@ -248,7 +248,7 @@ public class StealableArtifacts : GenericPersistence
public override void Serialize(IGenericWriter writer)
{
- writer.WriteEncodedInt(1); // version
+ writer.WriteEncodedInt(2); // version
writer.Write(_enabled);
@@ -261,7 +261,7 @@ public class StealableArtifacts : GenericPersistence
var si = _artifacts[i];
writer.Write(si.Item);
- writer.WriteDeltaTime(si.NextRespawn);
+ writer.WriteAnchoredTime(si.NextRespawn);
}
}
}
@@ -282,7 +282,7 @@ public class StealableArtifacts : GenericPersistence
for (var i = 0; i < length; i++)
{
var item = reader.ReadEntity
- ();
- var nextRespawn = reader.ReadDeltaTime();
+ var nextRespawn = version >= 2 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime();
if (i < _artifacts.Length)
{
diff --git a/Projects/UOContent/Engines/Virtues/VirtueContext.cs b/Projects/UOContent/Engines/Virtues/VirtueContext.cs
index 490002f43..7524f77ed 100644
--- a/Projects/UOContent/Engines/Virtues/VirtueContext.cs
+++ b/Projects/UOContent/Engines/Virtues/VirtueContext.cs
@@ -5,10 +5,29 @@ using Server.Mobiles;
namespace Server.Engines.Virtues;
[PropertyObject]
-[SerializationGenerator(0)]
+[SerializationGenerator(1)]
public partial class VirtueContext
{
- [DeltaDateTime]
+ private void MigrateFrom(V0Content content)
+ {
+ // Save-flagged values arrive as nullables; unset flags fall back to the same
+ // defaults the old deserialize left in place.
+ _lastSacrificeGain = content.LastSacrificeGain ?? default;
+ _lastSacrificeLoss = content.LastSacrificeLoss ?? default;
+ _availableResurrects = content.AvailableResurrects ?? 0;
+ _lastJusticeLoss = content.LastJusticeLoss ?? default;
+ _lastCompassionLoss = content.LastCompassionLoss ?? default;
+ _nextCompassionDay = content.NextCompassionDay ?? default;
+ _compassionGains = content.CompassionGains ?? 0;
+ _lastValorLoss = content.LastValorLoss ?? default;
+ _lastHonorUse = content.LastHonorUse ?? default;
+ _honorActive = content.HonorActive;
+ _justiceProtection = content.JusticeProtection;
+ _justiceStatus = content.JusticeStatus ?? JusticeProtectorStatus.None;
+ _values = content.Values;
+ }
+
+ [AnchoredDateTime]
[SerializableField(0)]
[SaveFlag(nameof(ShouldSerializeLastSacrificeGain))]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
@@ -16,7 +35,7 @@ public partial class VirtueContext
private bool ShouldSerializeLastSacrificeGain() => !SacrificeVirtue.CanGain(this);
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1)]
[SaveFlag(nameof(ShouldSerializeLastSacrificeLoss))]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
@@ -31,7 +50,7 @@ public partial class VirtueContext
private bool ShouldSerializeAvailableResurrects() => _availableResurrects > 0;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(3)]
[SaveFlag(nameof(ShouldSerializeLastJusticeLoss))]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
@@ -39,7 +58,7 @@ public partial class VirtueContext
private bool ShouldSerializeLastJusticeLoss() => !JusticeVirtue.CanAtrophy(this);
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(4)]
[SaveFlag(nameof(ShouldSerializeLastCompassionLoss))]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
@@ -47,7 +66,7 @@ public partial class VirtueContext
private bool ShouldSerializeLastCompassionLoss() => !CompassionVirtue.CanAtrophy(this);
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(5)]
[SaveFlag(nameof(ShouldSerializeNextCompassionDay))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
@@ -62,7 +81,7 @@ public partial class VirtueContext
private bool ShouldSerializeCompassionGains() => _compassionGains > 0;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(7)]
[SaveFlag(nameof(ShouldSerializeValorLoss))]
[SerializedCommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
@@ -70,7 +89,7 @@ public partial class VirtueContext
private bool ShouldSerializeValorLoss() => !ValorVirtue.CanAtrophy(this);
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(8)]
[SaveFlag(nameof(ShouldSerializeLastHonorUse))]
[SerializedCommandProperty(AccessLevel.GameMaster)]
diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs
new file mode 100644
index 000000000..bc9ce047e
--- /dev/null
+++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.Migrations.cs
@@ -0,0 +1,179 @@
+using System;
+
+namespace Server.Items;
+
+public partial class Corpse
+{
+ // Decay timer and TimeOfDeath moved from delta time to anchored time
+ private void MigrateFrom(V18Content content)
+ {
+ _restoreEquip = content.RestoreEquip;
+ _flags = content.Flags;
+ _timeOfDeath = content.TimeOfDeath;
+ _restoreTable = content.RestoreTable;
+ _looters = content.Looters;
+ _killer = content.Killer;
+ _aggressors = content.Aggressors;
+ _owner = content.Owner;
+ _corpseName = content.CorpseName;
+ _accessLevel = content.AccessLevel;
+ _guild = content.Guild;
+ _equipItems = content.EquipItems;
+ _hairItemId = content.HairItemId;
+ _hairHue = content.HairHue;
+ _facialHairItemId = content.FacialHairItemId;
+ _facialHairHue = content.FacialHairHue;
+
+ if (content.DecayTimerDelay != TimeSpan.MinValue)
+ {
+ DeserializeDecayTimer(content.DecayTimerDelay);
+ }
+ }
+
+ // Decay timer moved from [TimerDrift]/[DeserializeTimerField] to [DeserializeTimer]
+ private void MigrateFrom(V17Content content)
+ {
+ _restoreEquip = content.RestoreEquip;
+ _flags = content.Flags;
+ _timeOfDeath = content.TimeOfDeath;
+ _restoreTable = content.RestoreTable;
+ _looters = content.Looters;
+ _killer = content.Killer;
+ _aggressors = content.Aggressors;
+ _owner = content.Owner;
+ _corpseName = content.CorpseName;
+ _accessLevel = content.AccessLevel;
+ _guild = content.Guild;
+ _equipItems = content.EquipItems;
+ _hairItemId = content.HairItemId;
+ _hairHue = content.HairHue;
+ _facialHairItemId = content.FacialHairItemId;
+ _facialHairHue = content.FacialHairHue;
+
+ if (content.DecayTimerDelay != TimeSpan.MinValue)
+ {
+ DeserializeDecayTimer(content.DecayTimerDelay);
+ }
+ }
+
+ // Decomposed VirtualHairInfo into discrete int fields (hair/facial hair item id + hue)
+ private void MigrateFrom(V16Content content)
+ {
+ _restoreEquip = content.RestoreEquip;
+ _flags = content.Flags;
+ _timeOfDeath = content.TimeOfDeath;
+ _restoreTable = content.RestoreTable;
+ _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
+ _decayTimer.Start();
+ _looters = content.Looters;
+ _killer = content.Killer;
+ _aggressors = content.Aggressors;
+ _owner = content.Owner;
+ _corpseName = content.CorpseName;
+ _accessLevel = content.AccessLevel;
+ _guild = content.Guild;
+ _equipItems = content.EquipItems;
+ if (content.Hair != null)
+ {
+ _hairItemId = content.Hair.ItemId;
+ _hairHue = content.Hair.Hue;
+ }
+
+ if (content.FacialHair != null)
+ {
+ _facialHairItemId = content.FacialHair.ItemId;
+ _facialHairHue = content.FacialHair.Hue;
+ }
+ }
+
+ // Folded Murderer bool field into CorpseFlag.Murderer
+ private void MigrateFrom(V15Content content)
+ {
+ _restoreEquip = content.RestoreEquip;
+ _flags = content.Flags;
+ if (content.Murderer)
+ {
+ _flags |= CorpseFlag.Murderer;
+ }
+ _timeOfDeath = content.TimeOfDeath;
+ _restoreTable = content.RestoreTable;
+ _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
+ _decayTimer.Start();
+ _looters = content.Looters;
+ _killer = content.Killer;
+ _aggressors = content.Aggressors;
+ _owner = content.Owner;
+ _corpseName = content.CorpseName;
+ _accessLevel = content.AccessLevel;
+ _guild = content.Guild;
+ _equipItems = content.EquipItems;
+ if (content.Hair != null)
+ {
+ _hairItemId = content.Hair.ItemId;
+ _hairHue = content.Hair.Hue;
+ }
+
+ if (content.FacialHair != null)
+ {
+ _facialHairItemId = content.FacialHair.ItemId;
+ _facialHairHue = content.FacialHair.Hue;
+ }
+ }
+
+ // Replaced int Kills snapshot with bool Murderer snapshot
+ private void MigrateFrom(V14Content content)
+ {
+ _restoreEquip = content.RestoreEquip;
+ _flags = content.Flags;
+ if (content.Kills >= 5)
+ {
+ _flags |= CorpseFlag.Murderer;
+ }
+ _timeOfDeath = content.TimeOfDeath;
+ _restoreTable = content.RestoreTable;
+ _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
+ _decayTimer.Start();
+ _looters = content.Looters;
+ _killer = content.Killer;
+ _aggressors = content.Aggressors;
+ _owner = content.Owner;
+ _corpseName = content.CorpseName;
+ _accessLevel = content.AccessLevel;
+ _guild = content.Guild;
+ _equipItems = content.EquipItems;
+ if (content.Hair != null)
+ {
+ _hairItemId = content.Hair.ItemId;
+ _hairHue = content.Hair.Hue;
+ }
+
+ if (content.FacialHair != null)
+ {
+ _facialHairItemId = content.FacialHair.ItemId;
+ _facialHairHue = content.FacialHair.Hue;
+ }
+ }
+
+ // Added corpse hair and corpse facial hair
+ private void MigrateFrom(V13Content content)
+ {
+ _restoreEquip = content.RestoreEquip;
+ _flags = content.Flags;
+ if (content.Kills >= 5)
+ {
+ _flags |= CorpseFlag.Murderer;
+ }
+ _timeOfDeath = content.TimeOfDeath;
+ _restoreTable = content.RestoreTable;
+ _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
+ _decayTimer.Start();
+ _looters = content.Looters;
+ _killer = content.Killer;
+ _aggressors = content.Aggressors;
+ _owner = content.Owner;
+ _corpseName = content.CorpseName;
+ _accessLevel = content.AccessLevel;
+ _guild = content.Guild;
+ _equipItems = content.EquipItems;
+ }
+}
diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs
index 59db3dc98..04e9806c9 100644
--- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs
+++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs
@@ -86,7 +86,7 @@ public enum CorpseFlag
OwnerWasAnimatedDead = 0x00000800
}
-[SerializationGenerator(18, false)]
+[SerializationGenerator(19, false)]
public partial class Corpse : Container, ICarvable
{
public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0);
@@ -106,7 +106,7 @@ public partial class Corpse : Container, ICarvable
[SerializableField(1)]
private CorpseFlag _flags;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(2)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _timeOfDeath;
@@ -120,31 +120,6 @@ public partial class Corpse : Container, ICarvable
private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay);
- private void MigrateFrom(V17Content content)
- {
- _restoreEquip = content.RestoreEquip;
- _flags = content.Flags;
- _timeOfDeath = content.TimeOfDeath;
- _restoreTable = content.RestoreTable;
- _looters = content.Looters;
- _killer = content.Killer;
- _aggressors = content.Aggressors;
- _owner = content.Owner;
- _corpseName = content.CorpseName;
- _accessLevel = content.AccessLevel;
- _guild = content.Guild;
- _equipItems = content.EquipItems;
- _hairItemId = content.HairItemId;
- _hairHue = content.HairHue;
- _facialHairItemId = content.FacialHairItemId;
- _facialHairHue = content.FacialHairHue;
-
- if (content.DecayTimerDelay != TimeSpan.MinValue)
- {
- DeserializeDecayTimer(content.DecayTimerDelay);
- }
- }
-
[SerializableField(5, setter: "private")]
private HashSet _looters;
@@ -342,127 +317,6 @@ public partial class Corpse : Container, ICarvable
DevourCorpse();
}
- // Decomposed VirtualHairInfo into discrete int fields (hair/facial hair item id + hue)
- private void MigrateFrom(V16Content content)
- {
- _restoreEquip = content.RestoreEquip;
- _flags = content.Flags;
- _timeOfDeath = content.TimeOfDeath;
- _restoreTable = content.RestoreTable;
- _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
- _decayTimer.Start();
- _looters = content.Looters;
- _killer = content.Killer;
- _aggressors = content.Aggressors;
- _owner = content.Owner;
- _corpseName = content.CorpseName;
- _accessLevel = content.AccessLevel;
- _guild = content.Guild;
- _equipItems = content.EquipItems;
- if (content.Hair != null)
- {
- _hairItemId = content.Hair.ItemId;
- _hairHue = content.Hair.Hue;
- }
-
- if (content.FacialHair != null)
- {
- _facialHairItemId = content.FacialHair.ItemId;
- _facialHairHue = content.FacialHair.Hue;
- }
- }
-
- // Folded Murderer bool field into CorpseFlag.Murderer
- private void MigrateFrom(V15Content content)
- {
- _restoreEquip = content.RestoreEquip;
- _flags = content.Flags;
- if (content.Murderer)
- {
- _flags |= CorpseFlag.Murderer;
- }
- _timeOfDeath = content.TimeOfDeath;
- _restoreTable = content.RestoreTable;
- _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
- _decayTimer.Start();
- _looters = content.Looters;
- _killer = content.Killer;
- _aggressors = content.Aggressors;
- _owner = content.Owner;
- _corpseName = content.CorpseName;
- _accessLevel = content.AccessLevel;
- _guild = content.Guild;
- _equipItems = content.EquipItems;
- if (content.Hair != null)
- {
- _hairItemId = content.Hair.ItemId;
- _hairHue = content.Hair.Hue;
- }
-
- if (content.FacialHair != null)
- {
- _facialHairItemId = content.FacialHair.ItemId;
- _facialHairHue = content.FacialHair.Hue;
- }
- }
-
- // Replaced int Kills snapshot with bool Murderer snapshot
- private void MigrateFrom(V14Content content)
- {
- _restoreEquip = content.RestoreEquip;
- _flags = content.Flags;
- if (content.Kills >= 5)
- {
- _flags |= CorpseFlag.Murderer;
- }
- _timeOfDeath = content.TimeOfDeath;
- _restoreTable = content.RestoreTable;
- _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
- _decayTimer.Start();
- _looters = content.Looters;
- _killer = content.Killer;
- _aggressors = content.Aggressors;
- _owner = content.Owner;
- _corpseName = content.CorpseName;
- _accessLevel = content.AccessLevel;
- _guild = content.Guild;
- _equipItems = content.EquipItems;
- if (content.Hair != null)
- {
- _hairItemId = content.Hair.ItemId;
- _hairHue = content.Hair.Hue;
- }
-
- if (content.FacialHair != null)
- {
- _facialHairItemId = content.FacialHair.ItemId;
- _facialHairHue = content.FacialHair.Hue;
- }
- }
-
- // Added corpse hair and corpse facial hair
- private void MigrateFrom(V13Content content)
- {
- _restoreEquip = content.RestoreEquip;
- _flags = content.Flags;
- if (content.Kills >= 5)
- {
- _flags |= CorpseFlag.Murderer;
- }
- _timeOfDeath = content.TimeOfDeath;
- _restoreTable = content.RestoreTable;
- _decayTimer = new InternalTimer(this, content.DecayTimerDelay);
- _decayTimer.Start();
- _looters = content.Looters;
- _killer = content.Killer;
- _aggressors = content.Aggressors;
- _owner = content.Owner;
- _corpseName = content.CorpseName;
- _accessLevel = content.AccessLevel;
- _guild = content.Guild;
- _equipItems = content.EquipItems;
- }
-
[CommandProperty(AccessLevel.GameMaster)]
public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime;
diff --git a/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json b/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json
new file mode 100644
index 000000000..defcbe100
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Engines.CannedEvil.ChampionSpawn.v11.json
@@ -0,0 +1,189 @@
+{
+ "version": 11,
+ "type": "Server.Engines.CannedEvil.ChampionSpawn",
+ "properties": [
+ {
+ "name": "Level",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "ActivatedByProximity",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "NextProximityTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "MaxLevel",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "ActivatedByValor",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "DamageEntries",
+ "type": "System.Collections.Generic.Dictionary\u003CServer.Mobile, int\u003E",
+ "rule": "DictionaryMigrationRule",
+ "ruleArguments": [
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule",
+ "0",
+ "int",
+ "PrimitiveTypeMigrationRule",
+ "1",
+ ""
+ ]
+ },
+ {
+ "name": "ConfinedRoaming",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Idol",
+ "type": "Server.Engines.CannedEvil.IdolOfTheChampion",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "HasBeenAdvanced",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "SpawnArea",
+ "type": "Server.Rectangle2D",
+ "rule": "PrimitiveUOTypeMigrationRule",
+ "ruleArguments": [
+ "Rect2D"
+ ]
+ },
+ {
+ "name": "RandomizeType",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Kills",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Active",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Type",
+ "type": "Server.Engines.CannedEvil.ChampionSpawnType",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "Creatures",
+ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "RedSkulls",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "WhiteSkulls",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Platform",
+ "type": "Server.Engines.CannedEvil.ChampionPlatform",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Altar",
+ "type": "Server.Engines.CannedEvil.ChampionAltar",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "ExpireDelay",
+ "type": "System.TimeSpan",
+ "rule": "PrimitiveTypeMigrationRule"
+ },
+ {
+ "name": "ExpireTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "Champion",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "RestartDelay",
+ "type": "System.TimeSpan",
+ "rule": "PrimitiveTypeMigrationRule"
+ },
+ {
+ "name": "RestartTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json b/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json
new file mode 100644
index 000000000..956de91db
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Engines.Virtues.VirtueContext.v1.json
@@ -0,0 +1,119 @@
+{
+ "version": 1,
+ "type": "Server.Engines.Virtues.VirtueContext",
+ "properties": [
+ {
+ "name": "LastSacrificeGain",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "LastSacrificeLoss",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "AvailableResurrects",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "LastJusticeLoss",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "LastCompassionLoss",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "NextCompassionDay",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "CompassionGains",
+ "type": "int",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "LastValorLoss",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "LastHonorUse",
+ "type": "System.DateTime",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "HonorActive",
+ "type": "bool",
+ "usesSaveFlag": true,
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "JusticeProtection",
+ "type": "Server.Mobiles.PlayerMobile",
+ "usesSaveFlag": true,
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "JusticeStatus",
+ "type": "Server.Engines.Virtues.JusticeProtectorStatus",
+ "usesSaveFlag": true,
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "Values",
+ "type": "int[]",
+ "usesSaveFlag": true,
+ "rule": "ArrayMigrationRule",
+ "ruleArguments": [
+ "int",
+ "PrimitiveTypeMigrationRule",
+ ""
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json b/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json
new file mode 100644
index 000000000..04f4a0199
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Ethics.Player.v2.json
@@ -0,0 +1,50 @@
+{
+ "version": 2,
+ "type": "Server.Ethics.Player",
+ "properties": [
+ {
+ "name": "Mobile",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Power",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "History",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Steed",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Familiar",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Shield",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "Ethic",
+ "type": "Server.Ethics.Ethic",
+ "rule": "SerializableInterfaceMigrationRule"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json
new file mode 100644
index 000000000..2cccb267a
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Items.Corpse.v19.json
@@ -0,0 +1,137 @@
+{
+ "version": 19,
+ "type": "Server.Items.Corpse",
+ "properties": [
+ {
+ "name": "RestoreEquip",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Flags",
+ "type": "Server.Items.CorpseFlag",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "TimeOfDeath",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "RestoreTable",
+ "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E",
+ "rule": "DictionaryMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule",
+ "0",
+ "Server.Point3D",
+ "PrimitiveUOTypeMigrationRule",
+ "1",
+ "Point3D"
+ ]
+ },
+ {
+ "name": "DecayTimer",
+ "type": "Server.Timer",
+ "rule": "TimerMigrationRule",
+ "ruleArguments": [
+ "@AnchoredTimer"
+ ]
+ },
+ {
+ "name": "Looters",
+ "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E",
+ "rule": "HashSetMigrationRule",
+ "ruleArguments": [
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Killer",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Aggressors",
+ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Owner",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "CorpseName",
+ "type": "string",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "AccessLevel",
+ "type": "Server.AccessLevel",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "Guild",
+ "type": "Server.Guilds.Guild",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "EquipItems",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "HairItemId",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "HairHue",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "FacialHairItemId",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "FacialHairHue",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json
new file mode 100644
index 000000000..8424276f6
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Items.PuzzleChestSolutionAndTime.v1.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "type": "Server.Items.PuzzleChestSolutionAndTime",
+ "properties": [
+ {
+ "name": "When",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json b/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json
new file mode 100644
index 000000000..0a61f7bfe
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Items.StarRoomGate.v2.json
@@ -0,0 +1,22 @@
+{
+ "version": 2,
+ "type": "Server.Items.StarRoomGate",
+ "properties": [
+ {
+ "name": "Decays",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "DecayTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json b/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json
new file mode 100644
index 000000000..3745c4428
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Items.TransientItem.v2.json
@@ -0,0 +1,14 @@
+{
+ "version": 2,
+ "type": "Server.Items.TransientItem",
+ "properties": [
+ {
+ "name": "Expiration",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json b/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json
new file mode 100644
index 000000000..6b9c9eb1c
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Mobiles.PlayerVendor.v4.json
@@ -0,0 +1,62 @@
+{
+ "version": 4,
+ "type": "Server.Mobiles.PlayerVendor",
+ "properties": [
+ {
+ "name": "ShopName",
+ "type": "string",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "NextPayTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "House",
+ "type": "Server.Multis.BaseHouse",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Owner",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "BankAccount",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "HoldGold",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "SellItems",
+ "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Mobiles.VendorItem\u003E",
+ "rule": "DictionaryMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule",
+ "0",
+ "Server.Mobiles.VendorItem",
+ "RawSerializableMigrationRule",
+ "1",
+ ""
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json
new file mode 100644
index 000000000..b42fa33eb
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Mobiles.RentedVendor.v1.json
@@ -0,0 +1,62 @@
+{
+ "version": 1,
+ "type": "Server.Mobiles.RentedVendor",
+ "properties": [
+ {
+ "name": "RentalDurationId",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "RentalPrice",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "LandlordRenew",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "RenterRenew",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "RenewalPrice",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "RentalGold",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "RentalExpireTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json b/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json
new file mode 100644
index 000000000..bd224f77f
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Mobiles.Sheep.v1.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "type": "Server.Mobiles.Sheep",
+ "properties": [
+ {
+ "name": "NextWoolTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json b/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json
new file mode 100644
index 000000000..140d3dd64
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Multis.BaseBoat.v5.json
@@ -0,0 +1,73 @@
+{
+ "version": 5,
+ "type": "Server.Multis.BaseBoat",
+ "properties": [
+ {
+ "name": "MapItem",
+ "type": "Server.Items.MapItem",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "NextNavPoint",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Facing",
+ "type": "Server.Direction",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "TimeOfDecay",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ },
+ {
+ "name": "Owner",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "PPlank",
+ "type": "Server.Items.Plank",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "SPlank",
+ "type": "Server.Items.Plank",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "TillerMan",
+ "type": "Server.Items.TillerMan",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Hold",
+ "type": "Server.Items.Hold",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Anchored",
+ "type": "bool",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "ShipName",
+ "type": "string",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json b/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json
new file mode 100644
index 000000000..471c645f1
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Multis.BaseCamp.v2.json
@@ -0,0 +1,34 @@
+{
+ "version": 2,
+ "type": "Server.Multis.BaseCamp",
+ "properties": [
+ {
+ "name": "Items",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Mobiles",
+ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "@Tidy",
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "DecayTime",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json b/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json
new file mode 100644
index 000000000..1f16a4d7f
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Spells.Fifth.PoisonField.v1.json
@@ -0,0 +1,19 @@
+{
+ "version": 1,
+ "type": "Server.Spells.Fifth.PoisonField",
+ "properties": [
+ {
+ "name": "Caster",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "End",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json b/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json
new file mode 100644
index 000000000..3e91c7a4d
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Spells.Fourth.FireFieldItem.v1.json
@@ -0,0 +1,27 @@
+{
+ "version": 1,
+ "type": "Server.Spells.Fourth.FireFieldItem",
+ "properties": [
+ {
+ "name": "Damage",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "Caster",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "End",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json b/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json
new file mode 100644
index 000000000..dd202da03
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Spells.Seventh.EnergyField.v2.json
@@ -0,0 +1,19 @@
+{
+ "version": 2,
+ "type": "Server.Spells.Seventh.EnergyField",
+ "properties": [
+ {
+ "name": "Caster",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "End",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json b/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json
new file mode 100644
index 000000000..3e834a6b1
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Spells.Sixth.ParalyzeField.v1.json
@@ -0,0 +1,19 @@
+{
+ "version": 1,
+ "type": "Server.Spells.Sixth.ParalyzeField",
+ "properties": [
+ {
+ "name": "Caster",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "End",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json b/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json
new file mode 100644
index 000000000..eafcc5945
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Spells.Third.WallOfStone.v1.json
@@ -0,0 +1,19 @@
+{
+ "version": 1,
+ "type": "Server.Spells.Third.WallOfStone",
+ "properties": [
+ {
+ "name": "Caster",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "End",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "AnchoredTime"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs
index 32d0883cc..741093110 100644
--- a/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs
+++ b/Projects/UOContent/Mobiles/Animals/Misc/Sheep.cs
@@ -5,9 +5,14 @@ using System.Runtime.CompilerServices;
namespace Server.Mobiles
{
- [SerializationGenerator(0, false)]
+ [SerializationGenerator(1, false)]
public partial class Sheep : BaseCreature, ICarvable
{
+ private void MigrateFrom(V0Content content)
+ {
+ _nextWoolTime = content.NextWoolTime;
+ }
+
[Constructible]
public Sheep() : base(AIType.AI_Animal, FightMode.Aggressor)
{
@@ -44,7 +49,7 @@ namespace Server.Mobiles
public override string CorpseName => "a sheep corpse";
[SerializableField(0, fieldChanged: nameof(OnNextWoolTimeChanged))]
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _nextWoolTime;
diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs
index af85a195a..6c480ce18 100644
--- a/Projects/UOContent/Mobiles/BaseCreature.cs
+++ b/Projects/UOContent/Mobiles/BaseCreature.cs
@@ -1843,7 +1843,7 @@ namespace Server.Mobiles
{
base.Serialize(writer);
- writer.Write(20); // version
+ writer.Write(21); // version
writer.Write((int)m_CurrentAI);
writer.Write((int)m_DefaultAI);
@@ -1880,7 +1880,7 @@ namespace Server.Mobiles
if (_summoned)
{
- writer.WriteDeltaTime(SummonEnd);
+ writer.WriteAnchoredTime(SummonEnd);
}
writer.Write(ControlSlots);
@@ -2035,7 +2035,7 @@ namespace Server.Mobiles
if (_summoned)
{
- SummonEnd = reader.ReadDeltaTime();
+ SummonEnd = version >= 21 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime();
new UnsummonTimer(this, SummonEnd - Core.Now).Start();
}
diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs
index 5b878def2..9b8f5a2f3 100644
--- a/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs
+++ b/Projects/UOContent/Mobiles/Vendors/PlayerVendor.cs
@@ -22,9 +22,20 @@ public class PlayerVendorTargetAttribute : Attribute;
* Next, uncomment the MigrateFrom function and change the `V3Content` type to match the serialization version
* before it was bumped. Then run publish.cmd to generate the migration file.
*/
-[SerializationGenerator(3, false)]
+[SerializationGenerator(4, false)]
public partial class PlayerVendor : Mobile
{
+ private void MigrateFrom(V3Content content)
+ {
+ _shopName = content.ShopName;
+ _nextPayTime = content.NextPayTime;
+ _house = content.House;
+ _owner = content.Owner;
+ _bankAccount = content.BankAccount;
+ _holdGold = content.HoldGold;
+ _sellItems = content.SellItems;
+ }
+
private Timer _payTimer;
[InvalidateProperties]
@@ -32,7 +43,7 @@ public partial class PlayerVendor : Mobile
[SerializedCommandProperty(AccessLevel.GameMaster)]
private string _shopName;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1, setter: "private")]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _nextPayTime;
diff --git a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs
index 99d818826..d7d0a3ab6 100644
--- a/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs
+++ b/Projects/UOContent/Mobiles/Vendors/RentedVendor.cs
@@ -46,9 +46,20 @@ public class VendorRentalDuration
}
}
-[SerializationGenerator(0)]
+[SerializationGenerator(1)]
public partial class RentedVendor : PlayerVendor
{
+ private void MigrateFrom(V0Content content)
+ {
+ _rentalDurationId = content.RentalDurationId;
+ _rentalPrice = content.RentalPrice;
+ _landlordRenew = content.LandlordRenew;
+ _renterRenew = content.RenterRenew;
+ _renewalPrice = content.RenewalPrice;
+ _rentalGold = content.RentalGold;
+ _rentalExpireTime = content.RentalExpireTime;
+ }
+
private Timer _rentalExpireTimer;
public RentedVendor(
@@ -93,7 +104,7 @@ public partial class RentedVendor : PlayerVendor
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _rentalGold;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(6)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _rentalExpireTime;
diff --git a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs
index d4d5ce4bd..8f0a1fba8 100644
--- a/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs
+++ b/Projects/UOContent/Mobiles/Vendors/VendorInventory.cs
@@ -37,7 +37,7 @@ namespace Server.Mobiles
Items = reader.ReadEntityList
- ();
Gold = reader.ReadInt();
- ExpireTime = reader.ReadDeltaTime();
+ ExpireTime = version >= 1 ? reader.ReadAnchoredTime() : reader.ReadDeltaTime();
if (Items.Count == 0 && Gold == 0)
{
@@ -88,7 +88,7 @@ namespace Server.Mobiles
public void Serialize(IGenericWriter writer)
{
- writer.WriteEncodedInt(0); // version
+ writer.WriteEncodedInt(1); // version
writer.Write(Owner);
writer.Write(VendorName);
@@ -98,7 +98,7 @@ namespace Server.Mobiles
writer.Write(Items);
writer.Write(Gold);
- writer.WriteDeltaTime(ExpireTime);
+ writer.WriteAnchoredTime(ExpireTime);
}
private class ExpireTimer : Timer
diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs
index 9b2d4cc37..ed618b91a 100644
--- a/Projects/UOContent/Multis/Boats/BaseBoat.cs
+++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs
@@ -19,9 +19,24 @@ namespace Server.Multis
Single
}
- [SerializationGenerator(4, false)]
+ [SerializationGenerator(5, false)]
public abstract partial class BaseBoat : BaseMulti
{
+ private void MigrateFrom(V4Content content)
+ {
+ _mapItem = content.MapItem;
+ _nextNavPoint = content.NextNavPoint;
+ _facing = content.Facing;
+ _timeOfDecay = content.TimeOfDecay;
+ _owner = content.Owner;
+ _pPlank = content.PPlank;
+ _sPlank = content.SPlank;
+ _tillerMan = content.TillerMan;
+ _hold = content.Hold;
+ _anchored = content.Anchored;
+ _shipName = content.ShipName;
+ }
+
public enum DryDockResult
{
Valid,
@@ -137,7 +152,7 @@ namespace Server.Multis
}
[SerializableField(3, fieldChanged: nameof(OnTimeOfDecayChanged))]
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _timeOfDecay;
diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs
index e3d10dd0b..e60e3e46e 100644
--- a/Projects/UOContent/Multis/Camps/BaseCamp.cs
+++ b/Projects/UOContent/Multis/Camps/BaseCamp.cs
@@ -6,9 +6,16 @@ using Server.Mobiles;
namespace Server.Multis;
-[SerializationGenerator(1, false)]
+[SerializationGenerator(2, false)]
public abstract partial class BaseCamp : BaseMulti
{
+ private void MigrateFrom(V1Content content)
+ {
+ _items = content.Items;
+ _mobiles = content.Mobiles;
+ _decayTime = content.DecayTime;
+ }
+
[Tidy]
[SerializableField(0, setter: "private")]
private List
- _items;
@@ -17,7 +24,7 @@ public abstract partial class BaseCamp : BaseMulti
[SerializableField(1, setter: "private")]
private List _mobiles;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(2, setter: "private")]
private DateTime _decayTime;
diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs
index 3c5d2f609..92a797f19 100644
--- a/Projects/UOContent/Spells/Fifth/PoisonField.cs
+++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs
@@ -64,13 +64,19 @@ public class PoisonFieldSpell : MagerySpell, ITargetingSpell
}
[DispellableField]
-[SerializationGenerator(0, false)]
+[SerializationGenerator(1, false)]
public partial class PoisonField : Item
{
+ private void MigrateFrom(V0Content content)
+ {
+ _caster = content.Caster;
+ _end = content.End;
+ }
+
[SerializableField(0)]
private Mobile _caster;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1)]
private DateTime _end;
diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs
index 0b0a22129..8f20f995b 100644
--- a/Projects/UOContent/Spells/Fourth/FireField.cs
+++ b/Projects/UOContent/Spells/Fourth/FireField.cs
@@ -67,16 +67,23 @@ public class FireFieldSpell : MagerySpell, ITargetingSpell
}
[DispellableField]
-[SerializationGenerator(0, false)]
+[SerializationGenerator(1, false)]
public partial class FireFieldItem : Item
{
+ private void MigrateFrom(V0Content content)
+ {
+ _damage = content.Damage;
+ _caster = content.Caster;
+ _end = content.End;
+ }
+
[SerializableField(0)]
private int _damage;
[SerializableField(1)]
private Mobile _caster;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(2)]
private DateTime _end;
private Timer _timer;
diff --git a/Projects/UOContent/Spells/Seventh/EnergyField.cs b/Projects/UOContent/Spells/Seventh/EnergyField.cs
index 5e3ea1149..dbfece36d 100644
--- a/Projects/UOContent/Spells/Seventh/EnergyField.cs
+++ b/Projects/UOContent/Spells/Seventh/EnergyField.cs
@@ -77,13 +77,19 @@ public class EnergyFieldSpell : MagerySpell, ITargetingSpell
}
[DispellableField]
-[SerializationGenerator(1, false)]
+[SerializationGenerator(2, false)]
public partial class EnergyField : Item
{
+ private void MigrateFrom(V1Content content)
+ {
+ _caster = content.Caster;
+ _end = content.End;
+ }
+
[SerializableField(0)]
private Mobile _caster;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1)]
private DateTime _end;
diff --git a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs
index 830f16dfe..301cee507 100644
--- a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs
+++ b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs
@@ -77,13 +77,19 @@ public class ParalyzeFieldSpell : MagerySpell, ITargetingSpell
}
[DispellableField]
-[SerializationGenerator(0, false)]
+[SerializationGenerator(1, false)]
public partial class ParalyzeField : Item
{
+ private void MigrateFrom(V0Content content)
+ {
+ _caster = content.Caster;
+ _end = content.End;
+ }
+
[SerializableField(0)]
private Mobile _caster;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1)]
private DateTime _end;
diff --git a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs
index 54ac52030..49894cc95 100644
--- a/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs
+++ b/Projects/UOContent/Spells/Spellweaving/Items/TransientItem.cs
@@ -3,12 +3,17 @@ using ModernUO.Serialization;
namespace Server.Items;
-[SerializationGenerator(1, false)]
+[SerializationGenerator(2, false)]
public partial class TransientItem : Item
{
+ private void MigrateFrom(V1Content content)
+ {
+ _expiration = content.Expiration;
+ }
+
private TimerExecutionToken _timerToken;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(0)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private DateTime _expiration;
diff --git a/Projects/UOContent/Spells/Third/WallOfStone.cs b/Projects/UOContent/Spells/Third/WallOfStone.cs
index 7cae3b848..1b1dd04d3 100644
--- a/Projects/UOContent/Spells/Third/WallOfStone.cs
+++ b/Projects/UOContent/Spells/Third/WallOfStone.cs
@@ -63,13 +63,19 @@ public class WallOfStoneSpell : MagerySpell, ITargetingSpell
}
[DispellableField]
-[SerializationGenerator(0, false)]
+[SerializationGenerator(1, false)]
public partial class WallOfStone : Item
{
+ private void MigrateFrom(V0Content content)
+ {
+ _caster = content.Caster;
+ _end = content.End;
+ }
+
[SerializableField(0)]
private Mobile _caster;
- [DeltaDateTime]
+ [AnchoredDateTime]
[SerializableField(1)]
private DateTime _end;