Skip to content

Commit

Permalink
feat: Updates serialization to use MMF
Browse files Browse the repository at this point in the history
  • Loading branch information
kamronbatman committed Jun 22, 2024
1 parent b8ad5c6 commit b27947c
Show file tree
Hide file tree
Showing 26 changed files with 1,183 additions and 897 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"modernuoschemagenerator": {
"version": "2.10.9",
"version": "2.11.0",
"commands": [
"ModernUOSchemaGenerator"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ public Mobile this[int index]
}

public DateTime Created { get; set; }
public long SavePosition { get; set; }
public BufferWriter SaveBuffer { get; set; }
public Serial Serial { get; }
public void Deserialize(IGenericReader reader) => throw new NotImplementedException();

Expand Down
6 changes: 0 additions & 6 deletions Projects/Server/Guild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ protected BaseGuild()
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

[IgnoreDupe]
public long SavePosition { get; set; } = -1;

[IgnoreDupe]
public BufferWriter SaveBuffer { get; set; }

public abstract void Serialize(IGenericWriter writer);

public abstract void Deserialize(IGenericReader reader);
Expand Down
4 changes: 0 additions & 4 deletions Projects/Server/IEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ public Entity(Serial serial, Point3D loc, Map map) : this(serial)

public DateTime Created { get; set; } = Core.Now;

public long SavePosition { get; set; } = -1;

public BufferWriter SaveBuffer { get; set; }

public Serial Serial { get; }

public Point3D Location { get; private set; }
Expand Down
6 changes: 0 additions & 6 deletions Projects/Server/Items/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -783,12 +783,6 @@ public virtual void GetProperties(IPropertyList list)
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

[IgnoreDupe]
public long SavePosition { get; set; } = -1;

[IgnoreDupe]
public BufferWriter SaveBuffer { get; set; }

[IgnoreDupe]
[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }
Expand Down
11 changes: 8 additions & 3 deletions Projects/Server/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static class Core
private static bool _performProcessKill;
private static bool _restartOnKill;
private static bool _performSnapshot;
private static string _snapshotPath;
private static bool _crashed;
private static string _baseDirectory;

Expand Down Expand Up @@ -411,7 +412,6 @@ private static void HandleClosed()
logger.Information("Shutting down");

World.WaitForWriteCompletion();

World.ExitSerializationThreads();

if (!_crashed)
Expand Down Expand Up @@ -553,12 +553,13 @@ public static void RunEventLoop()
if (_performSnapshot)
{
// Return value is the offset that can be used to fix timers that should drift
World.Snapshot();
World.Snapshot(_snapshotPath);
_performSnapshot = false;
}

if (_performProcessKill)
{
World.WaitForWriteCompletion();
break;
}

Expand Down Expand Up @@ -594,7 +595,11 @@ public static void RunEventLoop()
DoKill(_restartOnKill);
}

internal static void RequestSnapshot() => _performSnapshot = true;
internal static void RequestSnapshot(string snapshotPath)
{
_performSnapshot = true;
_snapshotPath = snapshotPath;
}

public static void VerifySerialization()
{
Expand Down
6 changes: 0 additions & 6 deletions Projects/Server/Mobiles/Mobile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2262,12 +2262,6 @@ public virtual void GetProperties(IPropertyList list)
[CommandProperty(AccessLevel.GameMaster, readOnly: true)]
public DateTime Created { get; set; } = Core.Now;

[IgnoreDupe]
public long SavePosition { get; set; } = -1;

[IgnoreDupe]
public BufferWriter SaveBuffer { get; set; }

[IgnoreDupe]
[CommandProperty(AccessLevel.Counselor)]
public Serial Serial { get; }
Expand Down
62 changes: 30 additions & 32 deletions Projects/Server/Serialization/AdhocPersistence.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Copyright 2019-2024 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: AdhocPersistence.cs *
* *
Expand All @@ -25,11 +25,9 @@ namespace Server;
public static class AdhocPersistence
{
/**
* Serializes to memory synchronously. Optional buffer can be provided.
* Note: The buffer may not be the same after returning from the function if more data is written
* than the initial buffer can handle.
* Serializes to memory synchronously.
*/
public static BufferWriter Serialize(Action<IGenericWriter> serializer, ConcurrentQueue<Type> types)
public static IGenericWriter SerializeToBuffer(Action<IGenericWriter> serializer, ConcurrentQueue<Type> types = null)
{
var saveBuffer = new BufferWriter(true, types);
serializer(saveBuffer);
Expand All @@ -40,72 +38,72 @@ public static BufferWriter Serialize(Action<IGenericWriter> serializer, Concurre
* Writes a buffer to disk. This function should be called asynchronously.
* Writes the filePath for the binary data, and an accompanying SerializedTypes.db file of all possible types.
*/
public static void WriteSnapshot(FileInfo file, Span<byte> buffer)
public static void WriteSnapshot(string fileName, Span<byte> buffer)
{
var dirPath = file.DirectoryName;
PathUtility.EnsureDirectory(dirPath);

using var fs = new FileStream(file.FullName, FileMode.Create, FileAccess.Write);
using var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
fs.Write(buffer);
}

/**
* Serializes to a memory buffer synchronously, then flushes to the path asynchronously.
* See WriteSnapshot for more info about how to snapshot.
* Serializes to a Memory Mapped file synchronously, then flushes to the file asynchronously.
*/
public static void SerializeAndSnapshot(string filePath, Action<IGenericWriter> serializer, ConcurrentQueue<Type> types = null)
public static void SerializeAndSnapshot(string filePath, Action<IGenericWriter> serializer, long sizeHint = 1024 * 1024 * 32)
{
types ??= new ConcurrentQueue<Type>();
var saveBuffer = Serialize(serializer, types);
var fullPath = PathUtility.GetFullPath(filePath, Core.BaseDirectory);
PathUtility.EnsureDirectory(Path.GetDirectoryName(fullPath));
ConcurrentQueue<Type> types = [];
var writer = new MemoryMapFileWriter(new FileStream(filePath, FileMode.Create), sizeHint, types);
serializer(writer);

Task.Run(
() =>
{
var fullPath = PathUtility.GetFullPath(filePath, Core.BaseDirectory);
var file = new FileInfo(fullPath);
var fs = writer.FileStream;
WriteSnapshot(file, saveBuffer.Buffer.AsSpan(0, (int)saveBuffer.Position));
writer.Dispose();
fs.Dispose();
// TODO: Create a PooledHashSet if performance becomes an issue.
var typesSet = new HashSet<Type>();
HashSet<Type> typesSet = [];
// Dedupe the queue.
foreach (var type in types)
{
typesSet.Add(type);
}
Persistence.WriteSerializedTypesSnapshot(file.DirectoryName, typesSet);
Persistence.WriteSerializedTypesSnapshot(Path.GetDirectoryName(fullPath), typesSet);
});
}

public static void Deserialize(string filePath, Action<IGenericReader> deserializer)
public static unsafe void Deserialize(string filePath, Action<IGenericReader> deserializer)
{
var fullPath = PathUtility.GetFullPath(filePath, Core.BaseDirectory);
var file = new FileInfo(fullPath);

if (!file.Exists)
if (!file.Exists || file.Length == 0)
{
return;
}

var fileLength = file.Length;
if (fileLength == 0)
{
return;
}

string error;

try
{
using var mmf = MemoryMappedFile.CreateFromFile(fullPath, FileMode.Open);
using var stream = mmf.CreateViewStream();
using var br = new BinaryFileReader(stream);
deserializer(br);
using var accessor = mmf.CreateViewStream();

error = br.Position != fileLength
? $"Serialized {fileLength} bytes, but {br.Position} bytes deserialized"
byte* ptr = null;
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
UnmanagedDataReader dataReader = new UnmanagedDataReader(ptr, accessor.Length);
deserializer(dataReader);

error = dataReader.Position != fileLength
? $"Serialized {fileLength} bytes, but {dataReader.Position} bytes deserialized"
: null;

accessor.SafeMemoryMappedViewHandle.ReleasePointer();
}
catch (Exception e)
{
Expand Down
Loading

0 comments on commit b27947c

Please sign in to comment.