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
|
|
@ -30,6 +30,8 @@ namespace Server
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Close() => _reader.Close();
|
||||
|
||||
public DateTime LastSerialized { get; init; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadString(bool intern = false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ namespace Server
|
|||
|
||||
public override long Position => _position + Index;
|
||||
|
||||
|
||||
protected override int BufferSize => 81920;
|
||||
|
||||
public override void Flush()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ namespace Server
|
|||
_encoding = encoding ?? TextEncoding.UTF8;
|
||||
}
|
||||
|
||||
public BufferReader(byte[] buffer, DateTime LastSerialized) : this(buffer) => LastSerialized = LastSerialized;
|
||||
|
||||
public void Reset(byte[] newBuffer, out byte[] oldBuffer)
|
||||
{
|
||||
oldBuffer = _buffer;
|
||||
|
|
@ -45,6 +47,8 @@ namespace Server
|
|||
}
|
||||
|
||||
// Compatible with BinaryReader.ReadString()
|
||||
public DateTime LastSerialized { get; init; } = DateTime.MinValue;
|
||||
|
||||
public string ReadString(bool intern = false)
|
||||
{
|
||||
if (!ReadBool())
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ namespace Server
|
|||
{
|
||||
public interface IGenericReader
|
||||
{
|
||||
// Used to determine valid Entity deserialization
|
||||
DateTime LastSerialized { get; init; }
|
||||
|
||||
string ReadString(bool intern = false);
|
||||
long ReadLong();
|
||||
ulong ReadULong();
|
||||
|
|
|
|||
|
|
@ -39,14 +39,13 @@ namespace Server
|
|||
|
||||
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(),
|
||||
DateTimeKind.Unspecified => value.ToLocalTime().ToUniversalTime(),
|
||||
_ => value
|
||||
}).Ticks;
|
||||
value = value.ToUniversalTime();
|
||||
}
|
||||
|
||||
Write(ticks);
|
||||
Write(value.Ticks);
|
||||
}
|
||||
void WriteDeltaTime(DateTime value)
|
||||
{
|
||||
|
|
@ -59,17 +58,16 @@ namespace Server
|
|||
if (value == DateTime.MaxValue)
|
||||
{
|
||||
Write(long.MaxValue);
|
||||
return;
|
||||
}
|
||||
|
||||
var ticks = (value.Kind switch
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
DateTimeKind.Local => value.ToUniversalTime(),
|
||||
DateTimeKind.Unspecified => value.ToLocalTime().ToUniversalTime(),
|
||||
_ => value
|
||||
}).Ticks;
|
||||
value = value.ToUniversalTime();
|
||||
}
|
||||
|
||||
// Technically supports negative deltas for times in the past
|
||||
Write(ticks - DateTime.UtcNow.Ticks);
|
||||
Write(value.Ticks - DateTime.UtcNow.Ticks);
|
||||
}
|
||||
void Write(IPAddress value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,10 +20,20 @@ namespace Server
|
|||
{
|
||||
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; }
|
||||
BufferWriter SaveBuffer { get; protected internal set; }
|
||||
|
||||
int TypeRef { 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 Serialize(IGenericWriter writer);
|
||||
void Delete();
|
||||
|
|
@ -48,6 +58,8 @@ namespace Server
|
|||
{
|
||||
SaveBuffer ??= new BufferWriter(true);
|
||||
|
||||
BeforeSerialize();
|
||||
|
||||
// Clean, don't bother serializing
|
||||
if (SavePosition > -1)
|
||||
{
|
||||
|
|
@ -55,6 +67,7 @@ namespace Server
|
|||
return;
|
||||
}
|
||||
|
||||
LastSerialized = Core.Now;
|
||||
SaveBuffer.Seek(0, SeekOrigin.Begin);
|
||||
Serialize(SaveBuffer);
|
||||
|
||||
|
|
@ -64,7 +77,7 @@ namespace Server
|
|||
}
|
||||
else
|
||||
{
|
||||
SavePosition = -1;
|
||||
this.MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue