fix: Adds Created, LastSerialized, and BeforeSerialize for all entities. (#775)
* Adds `BeforeSerialized` for entities to handle cleanup. * Adds `Created` and `LastSerialized` fields to all entities. While this is a big bloat, this will be necessary for identifying dangling references to other invalid entities. * Changes formula for determining a valid reference to be _not null, not deleted, and reference's created date must be at or before the entities last serialized date_. * Adds versioning to idx file and serializes `Created` and `LastSerialized`. * Fixes Save Stats and also disables it by default.
This commit is contained in:
parent
3f6a87483b
commit
fa5eafdaff
23 changed files with 325 additions and 88 deletions
|
|
@ -63,13 +63,21 @@ namespace Server.Guilds
|
||||||
[CommandProperty(AccessLevel.Counselor)]
|
[CommandProperty(AccessLevel.Counselor)]
|
||||||
public Serial Serial { get; }
|
public Serial Serial { get; }
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
|
||||||
|
public DateTime Created { get; set; } = Core.Now;
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
DateTime ISerializable.LastSerialized { get; set; } = Core.Now;
|
||||||
|
|
||||||
long ISerializable.SavePosition { get; set; } = -1;
|
long ISerializable.SavePosition { get; set; } = -1;
|
||||||
|
|
||||||
BufferWriter ISerializable.SaveBuffer { get; set; }
|
BufferWriter ISerializable.SaveBuffer { get; set; }
|
||||||
|
|
||||||
public int TypeRef { get; private set; }
|
public int TypeRef { get; private set; }
|
||||||
|
|
||||||
|
public abstract void BeforeSerialize();
|
||||||
public abstract void Serialize(IGenericWriter writer);
|
public abstract void Serialize(IGenericWriter writer);
|
||||||
|
|
||||||
public abstract void Deserialize(IGenericReader reader);
|
public abstract void Deserialize(IGenericReader reader);
|
||||||
public abstract void OnDelete(Mobile mob);
|
public abstract void OnDelete(Mobile mob);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ namespace Server
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DateTime ISerializable.Created { get; set; } = Core.Now;
|
||||||
|
|
||||||
|
DateTime ISerializable.LastSerialized { get; set; } = DateTime.MaxValue;
|
||||||
|
|
||||||
long ISerializable.SavePosition { get; set; } = -1;
|
long ISerializable.SavePosition { get; set; } = -1;
|
||||||
|
|
||||||
BufferWriter ISerializable.SaveBuffer { get; set; }
|
BufferWriter ISerializable.SaveBuffer { get; set; }
|
||||||
|
|
@ -103,6 +107,10 @@ namespace Server
|
||||||
&& p.Y >= Location.m_Y - range
|
&& p.Y >= Location.m_Y - range
|
||||||
&& p.Y <= Location.m_Y + range;
|
&& p.Y <= Location.m_Y + range;
|
||||||
|
|
||||||
|
public void BeforeSerialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void Deserialize(IGenericReader reader)
|
public void Deserialize(IGenericReader reader)
|
||||||
{
|
{
|
||||||
// Should not actually be saved
|
// Should not actually be saved
|
||||||
|
|
|
||||||
|
|
@ -771,6 +771,12 @@ namespace Server
|
||||||
AddNameProperties(list);
|
AddNameProperties(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
|
||||||
|
public DateTime Created { get; set; } = Core.Now;
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
DateTime ISerializable.LastSerialized { get; set; } = Core.Now;
|
||||||
|
|
||||||
long ISerializable.SavePosition { get; set; } = -1;
|
long ISerializable.SavePosition { get; set; } = -1;
|
||||||
|
|
||||||
BufferWriter ISerializable.SaveBuffer { get; set; }
|
BufferWriter ISerializable.SaveBuffer { get; set; }
|
||||||
|
|
@ -2571,6 +2577,10 @@ namespace Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void BeforeSerialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Deserialize(IGenericReader reader)
|
public virtual void Deserialize(IGenericReader reader)
|
||||||
{
|
{
|
||||||
var version = reader.ReadInt();
|
var version = reader.ReadInt();
|
||||||
|
|
|
||||||
|
|
@ -1147,9 +1147,6 @@ namespace Server
|
||||||
|
|
||||||
public virtual bool ShouldCheckStatTimers => true;
|
public virtual bool ShouldCheckStatTimers => true;
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
|
||||||
public DateTime CreationTime { get; private set; }
|
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public int LightLevel
|
public int LightLevel
|
||||||
{
|
{
|
||||||
|
|
@ -2472,6 +2469,12 @@ namespace Server
|
||||||
AddNameProperties(list);
|
AddNameProperties(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
|
||||||
|
public DateTime Created { get; set; } = Core.Now;
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
DateTime ISerializable.LastSerialized { get; set; } = Core.Now;
|
||||||
|
|
||||||
long ISerializable.SavePosition { get; set; } = -1;
|
long ISerializable.SavePosition { get; set; } = -1;
|
||||||
|
|
||||||
BufferWriter ISerializable.SaveBuffer { get; set; }
|
BufferWriter ISerializable.SaveBuffer { get; set; }
|
||||||
|
|
@ -2483,7 +2486,7 @@ namespace Server
|
||||||
|
|
||||||
public virtual void Serialize(IGenericWriter writer)
|
public virtual void Serialize(IGenericWriter writer)
|
||||||
{
|
{
|
||||||
writer.Write(32); // version
|
writer.Write(33); // version
|
||||||
|
|
||||||
writer.WriteDeltaTime(LastStrGain);
|
writer.WriteDeltaTime(LastStrGain);
|
||||||
writer.WriteDeltaTime(LastIntGain);
|
writer.WriteDeltaTime(LastIntGain);
|
||||||
|
|
@ -2519,7 +2522,7 @@ namespace Server
|
||||||
|
|
||||||
writer.Write(Corpse);
|
writer.Write(Corpse);
|
||||||
|
|
||||||
writer.Write(CreationTime);
|
// writer.Write(CreationTime);
|
||||||
|
|
||||||
Stabled.Tidy();
|
Stabled.Tidy();
|
||||||
writer.Write(Stabled);
|
writer.Write(Stabled);
|
||||||
|
|
@ -6167,12 +6170,21 @@ namespace Server
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void BeforeSerialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Deserialize(IGenericReader reader)
|
public virtual void Deserialize(IGenericReader reader)
|
||||||
{
|
{
|
||||||
var version = reader.ReadInt();
|
var version = reader.ReadInt();
|
||||||
|
|
||||||
switch (version)
|
switch (version)
|
||||||
{
|
{
|
||||||
|
case 33:
|
||||||
|
{
|
||||||
|
// Removed created
|
||||||
|
goto case 32;
|
||||||
|
}
|
||||||
case 32:
|
case 32:
|
||||||
{
|
{
|
||||||
// Removed StuckMenu
|
// Removed StuckMenu
|
||||||
|
|
@ -6232,7 +6244,10 @@ namespace Server
|
||||||
}
|
}
|
||||||
case 23:
|
case 23:
|
||||||
{
|
{
|
||||||
CreationTime = reader.ReadDateTime();
|
if (version < 33)
|
||||||
|
{
|
||||||
|
Created = reader.ReadDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
goto case 22;
|
goto case 22;
|
||||||
}
|
}
|
||||||
|
|
@ -7818,7 +7833,6 @@ namespace Server
|
||||||
DamageEntries = new List<DamageEntry>();
|
DamageEntries = new List<DamageEntry>();
|
||||||
|
|
||||||
NextSkillTime = Core.TickCount;
|
NextSkillTime = Core.TickCount;
|
||||||
CreationTime = Core.Now;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Delta(MobileDelta flag)
|
public virtual void Delta(MobileDelta flag)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ namespace Server
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Close() => _reader.Close();
|
public void Close() => _reader.Close();
|
||||||
|
|
||||||
|
public DateTime LastSerialized { get; init; }
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public string ReadString(bool intern = false)
|
public string ReadString(bool intern = false)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ namespace Server
|
||||||
|
|
||||||
public override long Position => _position + Index;
|
public override long Position => _position + Index;
|
||||||
|
|
||||||
|
|
||||||
protected override int BufferSize => 81920;
|
protected override int BufferSize => 81920;
|
||||||
|
|
||||||
public override void Flush()
|
public override void Flush()
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ namespace Server
|
||||||
_encoding = encoding ?? TextEncoding.UTF8;
|
_encoding = encoding ?? TextEncoding.UTF8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BufferReader(byte[] buffer, DateTime LastSerialized) : this(buffer) => LastSerialized = LastSerialized;
|
||||||
|
|
||||||
public void Reset(byte[] newBuffer, out byte[] oldBuffer)
|
public void Reset(byte[] newBuffer, out byte[] oldBuffer)
|
||||||
{
|
{
|
||||||
oldBuffer = _buffer;
|
oldBuffer = _buffer;
|
||||||
|
|
@ -45,6 +47,8 @@ namespace Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compatible with BinaryReader.ReadString()
|
// Compatible with BinaryReader.ReadString()
|
||||||
|
public DateTime LastSerialized { get; init; } = DateTime.MinValue;
|
||||||
|
|
||||||
public string ReadString(bool intern = false)
|
public string ReadString(bool intern = false)
|
||||||
{
|
{
|
||||||
if (!ReadBool())
|
if (!ReadBool())
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ namespace Server
|
||||||
{
|
{
|
||||||
public interface IGenericReader
|
public interface IGenericReader
|
||||||
{
|
{
|
||||||
|
// Used to determine valid Entity deserialization
|
||||||
|
DateTime LastSerialized { get; init; }
|
||||||
|
|
||||||
string ReadString(bool intern = false);
|
string ReadString(bool intern = false);
|
||||||
long ReadLong();
|
long ReadLong();
|
||||||
ulong ReadULong();
|
ulong ReadULong();
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,13 @@ namespace Server
|
||||||
|
|
||||||
void Write(DateTime value)
|
void Write(DateTime value)
|
||||||
{
|
{
|
||||||
var ticks = (value.Kind switch
|
// If DateTimeKind is Unspecified, we can't assume it needs to be converted.
|
||||||
|
if (value.Kind == DateTimeKind.Local)
|
||||||
{
|
{
|
||||||
DateTimeKind.Local => value.ToUniversalTime(),
|
value = value.ToUniversalTime();
|
||||||
DateTimeKind.Unspecified => value.ToLocalTime().ToUniversalTime(),
|
}
|
||||||
_ => value
|
|
||||||
}).Ticks;
|
|
||||||
|
|
||||||
Write(ticks);
|
Write(value.Ticks);
|
||||||
}
|
}
|
||||||
void WriteDeltaTime(DateTime value)
|
void WriteDeltaTime(DateTime value)
|
||||||
{
|
{
|
||||||
|
|
@ -59,17 +58,16 @@ namespace Server
|
||||||
if (value == DateTime.MaxValue)
|
if (value == DateTime.MaxValue)
|
||||||
{
|
{
|
||||||
Write(long.MaxValue);
|
Write(long.MaxValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticks = (value.Kind switch
|
if (value.Kind == DateTimeKind.Local)
|
||||||
{
|
{
|
||||||
DateTimeKind.Local => value.ToUniversalTime(),
|
value = value.ToUniversalTime();
|
||||||
DateTimeKind.Unspecified => value.ToLocalTime().ToUniversalTime(),
|
}
|
||||||
_ => value
|
|
||||||
}).Ticks;
|
|
||||||
|
|
||||||
// Technically supports negative deltas for times in the past
|
// Technically supports negative deltas for times in the past
|
||||||
Write(ticks - DateTime.UtcNow.Ticks);
|
Write(value.Ticks - DateTime.UtcNow.Ticks);
|
||||||
}
|
}
|
||||||
void Write(IPAddress value)
|
void Write(IPAddress value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,20 @@ namespace Server
|
||||||
{
|
{
|
||||||
public interface ISerializable
|
public interface ISerializable
|
||||||
{
|
{
|
||||||
|
// Should be serialized/deserialized with the index so it can be referenced by IGenericReader
|
||||||
|
DateTime Created { get; set; }
|
||||||
|
|
||||||
|
// Should be serialized/deserialized with the index so it can be referenced by IGenericReader
|
||||||
|
DateTime LastSerialized { get; protected internal set; }
|
||||||
long SavePosition { get; protected internal set; }
|
long SavePosition { get; protected internal set; }
|
||||||
BufferWriter SaveBuffer { get; protected internal set; }
|
BufferWriter SaveBuffer { get; protected internal set; }
|
||||||
|
|
||||||
int TypeRef { get; }
|
int TypeRef { get; }
|
||||||
Serial Serial { get; }
|
Serial Serial { get; }
|
||||||
|
|
||||||
|
// Executed on every entity, before it's serialized.
|
||||||
|
// For example, this is used to clean up weak references and mark them dirty.
|
||||||
|
void BeforeSerialize();
|
||||||
void Deserialize(IGenericReader reader);
|
void Deserialize(IGenericReader reader);
|
||||||
void Serialize(IGenericWriter writer);
|
void Serialize(IGenericWriter writer);
|
||||||
void Delete();
|
void Delete();
|
||||||
|
|
@ -48,6 +58,8 @@ namespace Server
|
||||||
{
|
{
|
||||||
SaveBuffer ??= new BufferWriter(true);
|
SaveBuffer ??= new BufferWriter(true);
|
||||||
|
|
||||||
|
BeforeSerialize();
|
||||||
|
|
||||||
// Clean, don't bother serializing
|
// Clean, don't bother serializing
|
||||||
if (SavePosition > -1)
|
if (SavePosition > -1)
|
||||||
{
|
{
|
||||||
|
|
@ -55,6 +67,7 @@ namespace Server
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LastSerialized = Core.Now;
|
||||||
SaveBuffer.Seek(0, SeekOrigin.Begin);
|
SaveBuffer.Seek(0, SeekOrigin.Begin);
|
||||||
Serialize(SaveBuffer);
|
Serialize(SaveBuffer);
|
||||||
|
|
||||||
|
|
@ -64,7 +77,7 @@ namespace Server
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SavePosition = -1;
|
this.MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ namespace Server
|
||||||
{
|
{
|
||||||
public static class EntityPersistence
|
public static class EntityPersistence
|
||||||
{
|
{
|
||||||
|
private const int _idxVersion = 1;
|
||||||
|
|
||||||
public static void WriteEntities<I, T>(
|
public static void WriteEntities<I, T>(
|
||||||
IIndexInfo<I> indexInfo,
|
IIndexInfo<I> indexInfo,
|
||||||
Dictionary<I, T> entities,
|
Dictionary<I, T> entities,
|
||||||
|
|
@ -48,6 +50,7 @@ namespace Server
|
||||||
using var tdb = new BinaryFileWriter(tdbPath, false);
|
using var tdb = new BinaryFileWriter(tdbPath, false);
|
||||||
using var bin = new BinaryFileWriter(binPath, true);
|
using var bin = new BinaryFileWriter(binPath, true);
|
||||||
|
|
||||||
|
idx.Write(1); // Version
|
||||||
idx.Write(entities.Count);
|
idx.Write(entities.Count);
|
||||||
foreach (var e in entities.Values)
|
foreach (var e in entities.Values)
|
||||||
{
|
{
|
||||||
|
|
@ -55,6 +58,8 @@ namespace Server
|
||||||
|
|
||||||
idx.Write(e.TypeRef);
|
idx.Write(e.TypeRef);
|
||||||
idx.Write(e.Serial);
|
idx.Write(e.Serial);
|
||||||
|
idx.Write(e.Created.Ticks);
|
||||||
|
idx.Write(e.LastSerialized.Ticks);
|
||||||
idx.Write(start);
|
idx.Write(start);
|
||||||
|
|
||||||
e.SerializeTo(bin);
|
e.SerializeTo(bin);
|
||||||
|
|
@ -110,12 +115,28 @@ namespace Server
|
||||||
|
|
||||||
List<Tuple<ConstructorInfo, string>> types = ReadTypes<I>(tdbReader);
|
List<Tuple<ConstructorInfo, string>> types = ReadTypes<I>(tdbReader);
|
||||||
|
|
||||||
var count = idxReader.ReadInt32();
|
int count;
|
||||||
|
var version = idxReader.ReadInt32();
|
||||||
|
|
||||||
|
// Handle non-versioned (version 0).
|
||||||
|
if (version > _idxVersion || idx.Length - 4 - version * 20 == 0)
|
||||||
|
{
|
||||||
|
count = version;
|
||||||
|
version = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = idxReader.ReadInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
var typeID = idxReader.ReadInt32();
|
var typeID = idxReader.ReadInt32();
|
||||||
var number = idxReader.ReadUInt32();
|
var serial = idxReader.ReadUInt32();
|
||||||
|
var created = version == 0 ? now : new DateTime(idxReader.ReadInt64(), DateTimeKind.Utc);
|
||||||
|
var lastSerialized = version == 0 ? DateTime.MinValue : new DateTime(idxReader.ReadInt64(), DateTimeKind.Utc);
|
||||||
var pos = idxReader.ReadInt64();
|
var pos = idxReader.ReadInt64();
|
||||||
var length = idxReader.ReadInt32();
|
var length = idxReader.ReadInt32();
|
||||||
|
|
||||||
|
|
@ -127,12 +148,14 @@ namespace Server
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstructorInfo ctor = objs.Item1;
|
ConstructorInfo ctor = objs.Item1;
|
||||||
I indexer = indexInfo.CreateIndex(number);
|
I indexer = indexInfo.CreateIndex(serial);
|
||||||
|
|
||||||
ctorArgs[0] = indexer;
|
ctorArgs[0] = indexer;
|
||||||
|
|
||||||
if (ctor.Invoke(ctorArgs) is T t)
|
if (ctor.Invoke(ctorArgs) is T t)
|
||||||
{
|
{
|
||||||
|
t.Created = created;
|
||||||
|
t.LastSerialized = lastSerialized;
|
||||||
entities.Add(new EntityIndex<T>(t, typeID, pos, length));
|
entities.Add(new EntityIndex<T>(t, typeID, pos, length));
|
||||||
map[indexer] = t;
|
map[indexer] = t;
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +203,7 @@ namespace Server
|
||||||
var buffer = GC.AllocateUninitializedArray<byte>(entry.Length);
|
var buffer = GC.AllocateUninitializedArray<byte>(entry.Length);
|
||||||
if (br == null)
|
if (br == null)
|
||||||
{
|
{
|
||||||
br = new BufferReader(buffer);
|
br = new BufferReader(buffer, t.LastSerialized);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ namespace Server
|
||||||
|
|
||||||
private static string _tempSavePath; // Path to the temporary folder for the save
|
private static string _tempSavePath; // Path to the temporary folder for the save
|
||||||
private static string _savePath; // Path to "Saves" folder
|
private static string _savePath; // Path to "Saves" folder
|
||||||
|
private static bool _enableSaveStats;
|
||||||
|
|
||||||
public const bool DirtyTrackingEnabled = false;
|
public const bool DirtyTrackingEnabled = false;
|
||||||
public const uint ItemOffset = 0x40000000;
|
public const uint ItemOffset = 0x40000000;
|
||||||
|
|
@ -144,6 +145,7 @@ namespace Server
|
||||||
_tempSavePath = Path.Combine(Core.BaseDirectory, tempSavePath);
|
_tempSavePath = Path.Combine(Core.BaseDirectory, tempSavePath);
|
||||||
var savePath = ServerConfiguration.GetOrUpdateSetting("world.savePath", "Saves");
|
var savePath = ServerConfiguration.GetOrUpdateSetting("world.savePath", "Saves");
|
||||||
_savePath = Path.Combine(Core.BaseDirectory, savePath);
|
_savePath = Path.Combine(Core.BaseDirectory, savePath);
|
||||||
|
_enableSaveStats = ServerConfiguration.GetOrUpdateSetting("world.enableSaveStats", false);
|
||||||
|
|
||||||
// Mobiles & Items
|
// Mobiles & Items
|
||||||
Persistence.Register("Mobiles & Items", SaveEntities, WriteEntities, LoadEntities, 1);
|
Persistence.Register("Mobiles & Items", SaveEntities, WriteEntities, LoadEntities, 1);
|
||||||
|
|
@ -341,7 +343,10 @@ namespace Server
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
var timestamp = Utility.GetTimeStamp();
|
var timestamp = Utility.GetTimeStamp();
|
||||||
using var op = new StreamWriter("Logs/Saves/Save-Stats-{0}.log", true);
|
var saveStatsPath = Path.Combine(Core.BaseDirectory, $"Logs/Saves/Save-Stats-{timestamp}.log");
|
||||||
|
AssemblyHandler.EnsureDirectory(saveStatsPath);
|
||||||
|
|
||||||
|
using var op = new StreamWriter(saveStatsPath, true);
|
||||||
|
|
||||||
for (var i = 0; i < entityTypes.Length; i++)
|
for (var i = 0; i < entityTypes.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -373,7 +378,10 @@ namespace Server
|
||||||
EntityPersistence.WriteEntities(itemIndexInfo, Items, ItemTypes, basePath, out var itemCounts);
|
EntityPersistence.WriteEntities(itemIndexInfo, Items, ItemTypes, basePath, out var itemCounts);
|
||||||
EntityPersistence.WriteEntities(guildIndexInfo, Guilds, GuildTypes, basePath, out var guildCounts);
|
EntityPersistence.WriteEntities(guildIndexInfo, Guilds, GuildTypes, basePath, out var guildCounts);
|
||||||
|
|
||||||
TraceSave(mobileCounts?.ToList(), itemCounts?.ToList(), guildCounts?.ToList());
|
if (_enableSaveStats)
|
||||||
|
{
|
||||||
|
TraceSave(mobileCounts?.ToList(), itemCounts?.ToList(), guildCounts?.ToList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteFiles(object state)
|
public static void WriteFiles(object state)
|
||||||
|
|
@ -652,13 +660,24 @@ namespace Server
|
||||||
Serial serial = reader.ReadSerial();
|
Serial serial = reader.ReadSerial();
|
||||||
var typeT = typeof(T);
|
var typeT = typeof(T);
|
||||||
|
|
||||||
|
T entity;
|
||||||
|
|
||||||
// Add to this list when creating new serializable types
|
// Add to this list when creating new serializable types
|
||||||
if (typeof(BaseGuild).IsAssignableTo(typeT))
|
if (typeof(BaseGuild).IsAssignableTo(typeT))
|
||||||
{
|
{
|
||||||
return FindGuild(serial) as T;
|
entity = FindGuild(serial) as T;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entity = FindEntity<IEntity>(serial) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FindEntity<IEntity>(serial) as T;
|
if (entity?.Deleted == false)
|
||||||
|
{
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity?.Created <= reader.LastSerialized ? entity : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<T> ReadEntityList<T>(this IGenericReader reader) where T : class, ISerializable
|
public static List<T> ReadEntityList<T>(this IGenericReader reader) where T : class, ISerializable
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ using Server.Network;
|
||||||
|
|
||||||
namespace Server.Accounting
|
namespace Server.Accounting
|
||||||
{
|
{
|
||||||
[Serializable(3)]
|
[Serializable(4)]
|
||||||
public partial class Account : IAccount, IComparable<Account>, ISerializable
|
public partial class Account : IAccount, IComparable<Account>, ISerializable
|
||||||
{
|
{
|
||||||
public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0);
|
public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0);
|
||||||
|
|
@ -33,10 +33,7 @@ namespace Server.Accounting
|
||||||
[SerializableField(4)]
|
[SerializableField(4)]
|
||||||
private int _flags;
|
private int _flags;
|
||||||
|
|
||||||
[SerializableField(5, setter: "private")]
|
[SerializableField(5)]
|
||||||
private DateTime _created;
|
|
||||||
|
|
||||||
[SerializableField(6)]
|
|
||||||
private DateTime _lastLogin;
|
private DateTime _lastLogin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -44,7 +41,7 @@ namespace Server.Accounting
|
||||||
/// The value does not include the value of Platinum and ranges from
|
/// The value does not include the value of Platinum and ranges from
|
||||||
/// 0 to 999,999,999 by default.
|
/// 0 to 999,999,999 by default.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializableField(7, setter: "private")]
|
[SerializableField(6, setter: "private")]
|
||||||
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
|
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
|
||||||
public int _totalGold;
|
public int _totalGold;
|
||||||
|
|
||||||
|
|
@ -54,16 +51,16 @@ namespace Server.Accounting
|
||||||
/// 0 to 2,147,483,647 by default.
|
/// 0 to 2,147,483,647 by default.
|
||||||
/// One Platinum represents the value of CurrencyThreshold in Gold.
|
/// One Platinum represents the value of CurrencyThreshold in Gold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializableField(8, setter: "private")]
|
[SerializableField(7, setter: "private")]
|
||||||
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
|
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
|
||||||
public int _totalPlat;
|
public int _totalPlat;
|
||||||
|
|
||||||
[SerializableField(9, "private", "private")]
|
[SerializableField(8, "private", "private")]
|
||||||
private Mobile[] _mobiles;
|
private Mobile[] _mobiles;
|
||||||
|
|
||||||
private List<AccountComment> _comments;
|
private List<AccountComment> _comments;
|
||||||
|
|
||||||
[SerializableField(10)]
|
[SerializableField(9)]
|
||||||
public List<AccountComment> Comments
|
public List<AccountComment> Comments
|
||||||
{
|
{
|
||||||
get => _comments ??= new List<AccountComment>();
|
get => _comments ??= new List<AccountComment>();
|
||||||
|
|
@ -76,7 +73,7 @@ namespace Server.Accounting
|
||||||
|
|
||||||
private List<AccountTag> _tags;
|
private List<AccountTag> _tags;
|
||||||
|
|
||||||
[SerializableField(11)]
|
[SerializableField(10)]
|
||||||
public List<AccountTag> Tags
|
public List<AccountTag> Tags
|
||||||
{
|
{
|
||||||
get => _tags ??= new List<AccountTag>();
|
get => _tags ??= new List<AccountTag>();
|
||||||
|
|
@ -87,14 +84,14 @@ namespace Server.Accounting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableField(12)]
|
[SerializableField(11)]
|
||||||
private IPAddress[] _loginIPs;
|
private IPAddress[] _loginIPs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of IP addresses for restricted access. '*' wildcard supported. If the array contains zero entries, all IP addresses
|
/// List of IP addresses for restricted access. '*' wildcard supported. If the array contains zero entries, all IP addresses
|
||||||
/// are allowed.
|
/// are allowed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializableField(13)]
|
[SerializableField(12)]
|
||||||
private string[] _ipRestrictions;
|
private string[] _ipRestrictions;
|
||||||
|
|
||||||
private TimeSpan _totalGameTime;
|
private TimeSpan _totalGameTime;
|
||||||
|
|
@ -103,7 +100,7 @@ namespace Server.Accounting
|
||||||
/// Gets the total game time of this account, also considering the game time of characters
|
/// Gets the total game time of this account, also considering the game time of characters
|
||||||
/// that have been deleted.
|
/// that have been deleted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SerializableField(14)]
|
[SerializableField(13)]
|
||||||
public TimeSpan TotalGameTime
|
public TimeSpan TotalGameTime
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -125,7 +122,7 @@ namespace Server.Accounting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializableField(15)]
|
[SerializableField(14)]
|
||||||
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
|
[SerializableFieldAttr("[CommandProperty(AccessLevel.Administrator)]")]
|
||||||
private string _email;
|
private string _email;
|
||||||
|
|
||||||
|
|
@ -139,7 +136,7 @@ namespace Server.Accounting
|
||||||
|
|
||||||
_accessLevel = AccessLevel.Player;
|
_accessLevel = AccessLevel.Player;
|
||||||
|
|
||||||
_created = _lastLogin = Core.Now;
|
_lastLogin = Core.Now;
|
||||||
_totalGameTime = TimeSpan.Zero;
|
_totalGameTime = TimeSpan.Zero;
|
||||||
|
|
||||||
_mobiles = new Mobile[7];
|
_mobiles = new Mobile[7];
|
||||||
|
|
@ -184,7 +181,7 @@ namespace Server.Accounting
|
||||||
|
|
||||||
Enum.TryParse(Utility.GetText(node["accessLevel"], "Player"), true, out _accessLevel);
|
Enum.TryParse(Utility.GetText(node["accessLevel"], "Player"), true, out _accessLevel);
|
||||||
_flags = Utility.GetXMLInt32(Utility.GetText(node["flags"], "0"), 0);
|
_flags = Utility.GetXMLInt32(Utility.GetText(node["flags"], "0"), 0);
|
||||||
_created = Utility.GetXMLDateTime(Utility.GetText(node["created"], null), Core.Now);
|
Created = Utility.GetXMLDateTime(Utility.GetText(node["created"], null), Core.Now);
|
||||||
_lastLogin = Utility.GetXMLDateTime(Utility.GetText(node["lastLogin"], null), Core.Now);
|
_lastLogin = Utility.GetXMLDateTime(Utility.GetText(node["lastLogin"], null), Core.Now);
|
||||||
|
|
||||||
_totalGold = Utility.GetXMLInt32(Utility.GetText(node["totalGold"], "0"), 0);
|
_totalGold = Utility.GetXMLInt32(Utility.GetText(node["totalGold"], "0"), 0);
|
||||||
|
|
@ -306,10 +303,22 @@ namespace Server.Accounting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TimeSpan AccountAge => Core.Now - Created;
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
|
||||||
|
public DateTime Created { get; set; } = Core.Now;
|
||||||
|
|
||||||
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
DateTime ISerializable.LastSerialized { get; set; } = Core.Now;
|
||||||
|
|
||||||
public int TypeRef { get; private set; }
|
public int TypeRef { get; private set; }
|
||||||
|
|
||||||
public Serial Serial { get; set; }
|
public Serial Serial { get; set; }
|
||||||
|
|
||||||
|
public void BeforeSerialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[AfterDeserialization]
|
[AfterDeserialization]
|
||||||
private void AfterDeserialization()
|
private void AfterDeserialization()
|
||||||
{
|
{
|
||||||
|
|
@ -348,6 +357,26 @@ namespace Server.Accounting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MigrateFrom(V3Content content)
|
||||||
|
{
|
||||||
|
_username = content.Username;
|
||||||
|
_passwordAlgorithm = content.PasswordAlgorithm;
|
||||||
|
_password = content.Password;
|
||||||
|
_accessLevel = content.AccessLevel;
|
||||||
|
_flags = content.Flags;
|
||||||
|
Created = content.Created;
|
||||||
|
_lastLogin = content.LastLogin;
|
||||||
|
_totalGold = content.TotalGold;
|
||||||
|
_totalPlat = content.TotalPlat;
|
||||||
|
_mobiles = content.Mobiles;
|
||||||
|
_comments = content.Comments;
|
||||||
|
_tags = content.Tags;
|
||||||
|
_loginIPs = content.LoginIPs;
|
||||||
|
_ipRestrictions = content.IpRestrictions;
|
||||||
|
_totalGameTime = content.TotalGameTime;
|
||||||
|
_email = content.Email;
|
||||||
|
}
|
||||||
|
|
||||||
private void Deserialize(IGenericReader reader, int version)
|
private void Deserialize(IGenericReader reader, int version)
|
||||||
{
|
{
|
||||||
if (version != 2)
|
if (version != 2)
|
||||||
|
|
@ -361,7 +390,7 @@ namespace Server.Accounting
|
||||||
_password = reader.ReadString();
|
_password = reader.ReadString();
|
||||||
_accessLevel = version < 2 ? (AccessLevel)reader.ReadInt() : reader.ReadEnum<AccessLevel>();
|
_accessLevel = version < 2 ? (AccessLevel)reader.ReadInt() : reader.ReadEnum<AccessLevel>();
|
||||||
_flags = reader.ReadInt();
|
_flags = reader.ReadInt();
|
||||||
_created = reader.ReadDateTime();
|
Created = reader.ReadDateTime();
|
||||||
_lastLogin = reader.ReadDateTime();
|
_lastLogin = reader.ReadDateTime();
|
||||||
|
|
||||||
_totalGold = reader.ReadInt();
|
_totalGold = reader.ReadInt();
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ namespace Server.Misc
|
||||||
{
|
{
|
||||||
res = DeleteResultType.CharBeingPlayed;
|
res = DeleteResultType.CharBeingPlayed;
|
||||||
}
|
}
|
||||||
else if (RestrictDeletion && Core.Now < m.CreationTime + DeleteDelay)
|
else if (RestrictDeletion && Core.Now < m.Created + DeleteDelay)
|
||||||
{
|
{
|
||||||
res = DeleteResultType.CharTooYoung;
|
res = DeleteResultType.CharTooYoung;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,8 @@ namespace Server.Compression
|
||||||
{
|
{
|
||||||
_pathToTar ??= GetPathToTar();
|
_pathToTar ??= GetPathToTar();
|
||||||
|
|
||||||
var tarFlags = compressCommand == null ? "-axf" : "-xf";
|
|
||||||
var useExternalCompression = compressCommand != null ? $"--use-compress-program \"{compressCommand}\" " : "";
|
var useExternalCompression = compressCommand != null ? $"--use-compress-program \"{compressCommand}\" " : "";
|
||||||
var arguments = $"{useExternalCompression}{tarFlags} \"{fileNamePath}\" -C \"{outputDirectory}\"";
|
var arguments = $"{useExternalCompression} -xf \"{fileNamePath}\" -C \"{outputDirectory}\"";
|
||||||
|
|
||||||
return RunTar(arguments, compressionProgramPath) == 0;
|
return RunTar(arguments, compressionProgramPath) == 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -515,8 +515,7 @@ namespace Server.Mobiles
|
||||||
{
|
{
|
||||||
if (from.Account is Account acct && from.AccessLevel == AccessLevel.Player)
|
if (from.Account is Account acct && from.AccessLevel == AccessLevel.Player)
|
||||||
{
|
{
|
||||||
var time = TimeSpan.FromDays(RewardSystem.RewardInterval.TotalDays * 6) -
|
var time = TimeSpan.FromDays(RewardSystem.RewardInterval.TotalDays * 6) - acct.AccountAge;
|
||||||
(Core.Now - acct.Created);
|
|
||||||
|
|
||||||
if (time > TimeSpan.Zero)
|
if (time > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -59,15 +59,8 @@ namespace Server.Engines.VeteranRewards
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool HasAccess(Mobile mob, RewardEntry entry)
|
public static bool HasAccess(Mobile mob, RewardEntry entry) =>
|
||||||
{
|
Core.Expansion >= entry.RequiredExpansion && HasAccess(mob, entry.List, out var _);
|
||||||
if (Core.Expansion < entry.RequiredExpansion)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return HasAccess(mob, entry.List, out var _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasAccess(Mobile mob, RewardList list, out TimeSpan ts)
|
public static bool HasAccess(Mobile mob, RewardList list, out TimeSpan ts)
|
||||||
{
|
{
|
||||||
|
|
@ -77,13 +70,13 @@ namespace Server.Engines.VeteranRewards
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(mob.Account is Account acct))
|
if (mob.Account is not Account acct)
|
||||||
{
|
{
|
||||||
ts = TimeSpan.Zero;
|
ts = TimeSpan.Zero;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ts = list.Age - (Core.Now - acct.Created);
|
ts = list.Age - acct.AccountAge;
|
||||||
|
|
||||||
return ts <= TimeSpan.Zero;
|
return ts <= TimeSpan.Zero;
|
||||||
}
|
}
|
||||||
|
|
@ -98,12 +91,8 @@ namespace Server.Engines.VeteranRewards
|
||||||
return GetRewardLevel(acct);
|
return GetRewardLevel(acct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetRewardLevel(Account acct)
|
public static int GetRewardLevel(Account acct) =>
|
||||||
{
|
Math.Max((int)(acct.AccountAge.TotalDays / RewardInterval.TotalDays), 0);
|
||||||
var totalTime = Core.Now - acct.Created;
|
|
||||||
|
|
||||||
return Math.Max((int)(totalTime.TotalDays / RewardInterval.TotalDays), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HasHalfLevel(Mobile mob)
|
public static bool HasHalfLevel(Mobile mob)
|
||||||
{
|
{
|
||||||
|
|
@ -115,14 +104,7 @@ namespace Server.Engines.VeteranRewards
|
||||||
return HasHalfLevel(acct);
|
return HasHalfLevel(acct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool HasHalfLevel(Account acct)
|
public static bool HasHalfLevel(Account acct) => acct.AccountAge.TotalDays / RewardInterval.TotalDays >= 0.5;
|
||||||
{
|
|
||||||
var totalTime = Core.Now - acct.Created;
|
|
||||||
|
|
||||||
var level = totalTime.TotalDays / RewardInterval.TotalDays;
|
|
||||||
|
|
||||||
return level >= 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ConsumeRewardPoint(Mobile mob)
|
public static bool ConsumeRewardPoint(Mobile mob)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ namespace Server.Items
|
||||||
|
|
||||||
m_Entries = new List<CampfireEntry>();
|
m_Entries = new List<CampfireEntry>();
|
||||||
|
|
||||||
Created = Core.Now;
|
|
||||||
Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick, out _timerToken);
|
Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnTick, out _timerToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,9 +36,6 @@ namespace Server.Items
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
|
||||||
public DateTime Created { get; }
|
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public CampfireStatus Status
|
public CampfireStatus Status
|
||||||
{
|
{
|
||||||
|
|
|
||||||
127
Projects/UOContent/Migrations/Server.Accounting.Account.v4.json
Normal file
127
Projects/UOContent/Migrations/Server.Accounting.Account.v4.json
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"type": "Server.Accounting.Account",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "Username",
|
||||||
|
"type": "string",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PasswordAlgorithm",
|
||||||
|
"type": "Server.Accounting.Security.PasswordProtectionAlgorithm",
|
||||||
|
"rule": "EnumMigrationRule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Password",
|
||||||
|
"type": "string",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AccessLevel",
|
||||||
|
"type": "Server.AccessLevel",
|
||||||
|
"rule": "EnumMigrationRule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Flags",
|
||||||
|
"type": "int",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LastLogin",
|
||||||
|
"type": "System.DateTime",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TotalGold",
|
||||||
|
"type": "int",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TotalPlat",
|
||||||
|
"type": "int",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mobiles",
|
||||||
|
"type": "Server.Mobile[]",
|
||||||
|
"rule": "ArrayMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"Server.Mobile",
|
||||||
|
"SerializableInterfaceMigrationRule"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comments",
|
||||||
|
"type": "System.Collections.Generic.List\u003CServer.Accounting.AccountComment\u003E",
|
||||||
|
"rule": "ListMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"",
|
||||||
|
"Server.Accounting.AccountComment",
|
||||||
|
"SerializationMethodSignatureMigrationRule",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tags",
|
||||||
|
"type": "System.Collections.Generic.List\u003CServer.Accounting.AccountTag\u003E",
|
||||||
|
"rule": "ListMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"",
|
||||||
|
"Server.Accounting.AccountTag",
|
||||||
|
"SerializationMethodSignatureMigrationRule",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "LoginIPs",
|
||||||
|
"type": "System.Net.IPAddress[]",
|
||||||
|
"rule": "ArrayMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"System.Net.IPAddress",
|
||||||
|
"PrimitiveTypeMigrationRule"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IpRestrictions",
|
||||||
|
"type": "string[]",
|
||||||
|
"rule": "ArrayMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"string",
|
||||||
|
"PrimitiveTypeMigrationRule",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TotalGameTime",
|
||||||
|
"type": "System.TimeSpan",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Email",
|
||||||
|
"type": "string",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Server.Misc
|
||||||
|
|
||||||
if (Required != null && version < Required && (m_OldClientResponse == OldClientResponse.Kick ||
|
if (Required != null && version < Required && (m_OldClientResponse == OldClientResponse.Kick ||
|
||||||
m_OldClientResponse == OldClientResponse.LenientKick &&
|
m_OldClientResponse == OldClientResponse.LenientKick &&
|
||||||
Core.Now - state.Mobile.CreationTime > m_AgeLeniency &&
|
Core.Now - state.Mobile.Created > m_AgeLeniency &&
|
||||||
state.Mobile is PlayerMobile mobile &&
|
state.Mobile is PlayerMobile mobile &&
|
||||||
mobile.GameTime > m_GameTimeLeniency))
|
mobile.GameTime > m_GameTimeLeniency))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1156,6 +1156,10 @@ namespace Server.Guilds
|
||||||
list.TrimExcess();
|
list.TrimExcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void BeforeSerialize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public override void Serialize(IGenericWriter writer)
|
public override void Serialize(IGenericWriter writer)
|
||||||
{
|
{
|
||||||
if (LastFealty + TimeSpan.FromDays(1.0) < Core.Now)
|
if (LastFealty + TimeSpan.FromDays(1.0) < Core.Now)
|
||||||
|
|
|
||||||
|
|
@ -69,24 +69,24 @@ namespace Server.Misc
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
var ts = Core.Now - a.Created;
|
var age = a.AccountAge;
|
||||||
|
|
||||||
if (Format(ts.TotalDays, "This account is {0} day{1} old.", out var v))
|
if (Format(age.TotalDays, "This account is {0} day{1} old.", out var v))
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Format(ts.TotalHours, "This account is {0} hour{1} old.", out v))
|
if (Format(age.TotalHours, "This account is {0} hour{1} old.", out v))
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Format(ts.TotalMinutes, "This account is {0} minute{1} old.", out v))
|
if (Format(age.TotalMinutes, "This account is {0} minute{1} old.", out v))
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Format(ts.TotalSeconds, "This account is {0} second{1} old.", out v))
|
if (Format(age.TotalSeconds, "This account is {0} second{1} old.", out v))
|
||||||
{
|
{
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ namespace Server.Mobiles
|
||||||
{
|
{
|
||||||
SayTo(pm, 501046); // Thou must resign from thy other guild first.
|
SayTo(pm, 501046); // Thou must resign from thy other guild first.
|
||||||
}
|
}
|
||||||
else if (pm.GameTime < JoinGameAge || pm.CreationTime + JoinAge > Core.Now)
|
else if (pm.GameTime < JoinGameAge || pm.Created + JoinAge > Core.Now)
|
||||||
{
|
{
|
||||||
SayTo(pm, 501048); // You are too young to join my guild...
|
SayTo(pm, 501048); // You are too young to join my guild...
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +141,7 @@ namespace Server.Mobiles
|
||||||
{
|
{
|
||||||
SayTo(pm, 501046); // Thou must resign from thy other guild first.
|
SayTo(pm, 501046); // Thou must resign from thy other guild first.
|
||||||
}
|
}
|
||||||
else if (pm.GameTime < JoinGameAge || pm.CreationTime + JoinAge > Core.Now)
|
else if (pm.GameTime < JoinGameAge || pm.Created + JoinAge > Core.Now)
|
||||||
{
|
{
|
||||||
SayTo(pm, 501048); // You are too young to join my guild...
|
SayTo(pm, 501048); // You are too young to join my guild...
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue