fix: Cleans up core code (#1187)
**Only one functional change** * Fixes a bug in LogFactory where `Warning` is being logged as `Information` Non-functional changes: * Updates/Fixes copyright headers * Removes namespace scopes for core files. View with [whitespace off](https://github.com/modernuo/ModernUO/pull/1187/files?w=1).
This commit is contained in:
parent
0138d40bda
commit
f268d5d4e2
262 changed files with 28527 additions and 28646 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: AdhocPersistence.cs *
|
||||
* *
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: ManualDirtyCheckingAttribute.cs *
|
||||
* *
|
||||
|
|
@ -15,14 +15,13 @@
|
|||
|
||||
using System;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the applied class has dirty checking. This is necessary for classes that are not code genned.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ManualDirtyCheckingAttribute : Attribute
|
||||
{
|
||||
namespace Server;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the applied class has dirty checking. This is necessary for classes that are not code genned.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ManualDirtyCheckingAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: BinaryFileReader.cs *
|
||||
* *
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: BufferedFileWriter.cs *
|
||||
* File: BinaryFileWriter.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
|
|
@ -16,55 +16,54 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public class BinaryFileWriter : BufferWriter, IDisposable
|
||||
{
|
||||
public class BinaryFileWriter : BufferWriter, IDisposable
|
||||
private readonly Stream _file;
|
||||
private long _position;
|
||||
|
||||
public BinaryFileWriter(string filename, bool prefixStr) :
|
||||
this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), prefixStr)
|
||||
{}
|
||||
|
||||
public BinaryFileWriter(Stream stream, bool prefixStr) : base(prefixStr)
|
||||
{
|
||||
private readonly Stream _file;
|
||||
private long _position;
|
||||
_file = stream;
|
||||
_position = _file.Position;
|
||||
}
|
||||
|
||||
public BinaryFileWriter(string filename, bool prefixStr) :
|
||||
this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), prefixStr)
|
||||
{}
|
||||
public override long Position => _position + Index;
|
||||
|
||||
public BinaryFileWriter(Stream stream, bool prefixStr) : base(prefixStr)
|
||||
protected override int BufferSize => 81920;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if (Index > 0)
|
||||
{
|
||||
_file = stream;
|
||||
_position = _file.Position;
|
||||
_position += Index;
|
||||
|
||||
_file.Write(Buffer, 0, (int)Index);
|
||||
Index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Position => _position + Index;
|
||||
|
||||
protected override int BufferSize => 81920;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
if (Index > 0)
|
||||
{
|
||||
_position += Index;
|
||||
|
||||
_file.Write(Buffer, 0, (int)Index);
|
||||
Index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (Index > 0)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
|
||||
_file.Close();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
public override void Close()
|
||||
{
|
||||
if (Index > 0)
|
||||
{
|
||||
Flush();
|
||||
|
||||
return _position = _file.Seek(offset, origin);
|
||||
}
|
||||
|
||||
public void Dispose() => Close();
|
||||
_file.Close();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
Flush();
|
||||
|
||||
return _position = _file.Seek(offset, origin);
|
||||
}
|
||||
|
||||
public void Dispose() => Close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: BufferReader.cs *
|
||||
* *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: BufferedFileWriter.cs *
|
||||
* *
|
||||
|
|
@ -21,312 +21,311 @@ using System.Text;
|
|||
using Server.Collections;
|
||||
using Server.Text;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public class BufferWriter : IGenericWriter
|
||||
{
|
||||
public class BufferWriter : IGenericWriter
|
||||
private readonly Encoding m_Encoding;
|
||||
private readonly bool m_PrefixStrings;
|
||||
private long _bytesWritten;
|
||||
private long _index;
|
||||
|
||||
protected long Index
|
||||
{
|
||||
private readonly Encoding m_Encoding;
|
||||
private readonly bool m_PrefixStrings;
|
||||
private long _bytesWritten;
|
||||
private long _index;
|
||||
|
||||
protected long Index
|
||||
get => _index;
|
||||
set
|
||||
{
|
||||
get => _index;
|
||||
set
|
||||
if (value < 0 || value > _buffer.Length)
|
||||
{
|
||||
if (value < 0 || value > _buffer.Length)
|
||||
{
|
||||
// If you are receiving this exception and your value is too large, you may need to use `Resize`
|
||||
// If you are receiving this exception and your value is negative, you probably used Seek incorrectly.
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
_index = value;
|
||||
|
||||
if (value > _bytesWritten)
|
||||
{
|
||||
_bytesWritten = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _buffer;
|
||||
|
||||
public BufferWriter(byte[] buffer, bool prefixStr)
|
||||
{
|
||||
m_PrefixStrings = prefixStr;
|
||||
m_Encoding = TextEncoding.UTF8;
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public BufferWriter(bool prefixStr) : this(0, prefixStr)
|
||||
{
|
||||
}
|
||||
|
||||
public BufferWriter(int count, bool prefixStr)
|
||||
{
|
||||
m_PrefixStrings = prefixStr;
|
||||
m_Encoding = TextEncoding.UTF8;
|
||||
_buffer = GC.AllocateUninitializedArray<byte>(count < 1 ? BufferSize : count);
|
||||
}
|
||||
|
||||
public virtual long Position => Index;
|
||||
|
||||
protected virtual int BufferSize => 256;
|
||||
|
||||
public byte[] Buffer => _buffer;
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Resize(int size)
|
||||
{
|
||||
// We shouldn't ever resize to a 0 length buffer. That is dangerous
|
||||
if (size <= 0)
|
||||
{
|
||||
size = BufferSize;
|
||||
// If you are receiving this exception and your value is too large, you may need to use `Resize`
|
||||
// If you are receiving this exception and your value is negative, you probably used Seek incorrectly.
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
if (size < _buffer.Length)
|
||||
_index = value;
|
||||
|
||||
if (value > _bytesWritten)
|
||||
{
|
||||
_bytesWritten = size;
|
||||
}
|
||||
|
||||
var newBuffer = GC.AllocateUninitializedArray<byte>(size);
|
||||
_buffer.AsSpan(0, Math.Min(size, _buffer.Length)).CopyTo(newBuffer);
|
||||
_buffer = newBuffer;
|
||||
}
|
||||
|
||||
public virtual void Flush()
|
||||
{
|
||||
// Need to avoid buffer.Length = 2, buffer * 2 is 4, but we need 8 or 16bytes, causing an exception.
|
||||
// The least we need is 16bytes + Index, but we use BufferSize since it should always be big enough for a single
|
||||
// non-dynamic field.
|
||||
Resize(Math.Max(BufferSize, _buffer.Length * 2));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void FlushIfNeeded(int amount)
|
||||
{
|
||||
if (Index + amount > _buffer.Length)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
var remaining = bytes.Length;
|
||||
var idx = 0;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
FlushIfNeeded(remaining);
|
||||
|
||||
var count = Math.Min((int)(_buffer.Length - Index), remaining);
|
||||
bytes.Slice(idx, count).CopyTo(_buffer.AsSpan((int)Index, count));
|
||||
|
||||
idx += count;
|
||||
Index += count;
|
||||
remaining -= count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BitArray bitArray)
|
||||
{
|
||||
var byteLength = BitArray.GetByteArrayLengthFromBitLength(bitArray.Length);
|
||||
|
||||
((IGenericWriter)this).WriteEncodedInt(bitArray.Length);
|
||||
FlushIfNeeded(byteLength);
|
||||
bitArray.CopyTo(_buffer.AsSpan((int)Index, byteLength));
|
||||
Index += byteLength;
|
||||
}
|
||||
|
||||
public virtual long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.End || offset <= 0 && offset > -_buffer.Length,
|
||||
"Attempting to seek to an invalid position using SeekOrigin.End"
|
||||
);
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.Begin || offset >= 0 && offset < _buffer.Length,
|
||||
"Attempting to seek to an invalid position using SeekOrigin.Begin"
|
||||
);
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.Current || Index + offset >= 0 && Index + offset < _buffer.Length,
|
||||
"Attempting to seek to an invalid position using SeekOrigin.Current"
|
||||
);
|
||||
|
||||
return Index = Math.Max(0, origin switch
|
||||
{
|
||||
SeekOrigin.Current => Index + offset,
|
||||
SeekOrigin.End => _bytesWritten + offset,
|
||||
_ => offset // Begin
|
||||
});
|
||||
}
|
||||
|
||||
public void Write(string value)
|
||||
{
|
||||
if (m_PrefixStrings)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Write(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(true);
|
||||
InternalWriteString(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InternalWriteString(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(long value)
|
||||
{
|
||||
FlushIfNeeded(8);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
_buffer[Index++] = (byte)(value >> 32);
|
||||
_buffer[Index++] = (byte)(value >> 40);
|
||||
_buffer[Index++] = (byte)(value >> 48);
|
||||
_buffer[Index++] = (byte)(value >> 56);
|
||||
}
|
||||
|
||||
public void Write(ulong value)
|
||||
{
|
||||
FlushIfNeeded(8);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
_buffer[Index++] = (byte)(value >> 32);
|
||||
_buffer[Index++] = (byte)(value >> 40);
|
||||
_buffer[Index++] = (byte)(value >> 48);
|
||||
_buffer[Index++] = (byte)(value >> 56);
|
||||
}
|
||||
|
||||
public void Write(int value)
|
||||
{
|
||||
FlushIfNeeded(4);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
}
|
||||
|
||||
public void Write(uint value)
|
||||
{
|
||||
FlushIfNeeded(4);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
}
|
||||
|
||||
public void Write(short value)
|
||||
{
|
||||
FlushIfNeeded(2);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
}
|
||||
|
||||
public void Write(ushort value)
|
||||
{
|
||||
FlushIfNeeded(2);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
}
|
||||
|
||||
public unsafe void Write(double value)
|
||||
{
|
||||
FlushIfNeeded(8);
|
||||
|
||||
fixed (byte* pBuffer = _buffer)
|
||||
{
|
||||
*(double*)(pBuffer + Index) = value;
|
||||
}
|
||||
|
||||
Index += 8;
|
||||
}
|
||||
|
||||
public unsafe void Write(float value)
|
||||
{
|
||||
FlushIfNeeded(4);
|
||||
|
||||
fixed (byte* pBuffer = _buffer)
|
||||
{
|
||||
*(float*)(pBuffer + Index) = value;
|
||||
}
|
||||
|
||||
Index += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(byte value)
|
||||
{
|
||||
FlushIfNeeded(1);
|
||||
_buffer[Index++] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(sbyte value)
|
||||
{
|
||||
FlushIfNeeded(1);
|
||||
_buffer[Index++] = (byte)value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe void Write(bool value)
|
||||
{
|
||||
FlushIfNeeded(1);
|
||||
_buffer[Index++] = *(byte*)&value; // up to 30% faster to dereference the raw value on the stack
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(Serial serial) => Write(serial.Value);
|
||||
|
||||
internal void InternalWriteString(string value)
|
||||
{
|
||||
var remaining = m_Encoding.GetByteCount(value);
|
||||
|
||||
((IGenericWriter)this).WriteEncodedInt(remaining);
|
||||
|
||||
if (remaining == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// It is much faster to encode to stack buffer, then copy to the real buffer
|
||||
Span<byte> span = stackalloc byte[Math.Min(BufferSize, 256)];
|
||||
var maxChars = span.Length / m_Encoding.GetMaxByteCount(1);
|
||||
var charsLeft = value.Length;
|
||||
var current = 0;
|
||||
|
||||
while (charsLeft > 0)
|
||||
{
|
||||
var charCount = Math.Min(charsLeft, maxChars);
|
||||
var bytesWritten = m_Encoding.GetBytes(value.AsSpan(current, charCount), span);
|
||||
remaining -= bytesWritten;
|
||||
charsLeft -= charCount;
|
||||
current += charCount;
|
||||
|
||||
Write(span[..bytesWritten]);
|
||||
_bytesWritten = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _buffer;
|
||||
|
||||
public BufferWriter(byte[] buffer, bool prefixStr)
|
||||
{
|
||||
m_PrefixStrings = prefixStr;
|
||||
m_Encoding = TextEncoding.UTF8;
|
||||
_buffer = buffer;
|
||||
}
|
||||
|
||||
public BufferWriter(bool prefixStr) : this(0, prefixStr)
|
||||
{
|
||||
}
|
||||
|
||||
public BufferWriter(int count, bool prefixStr)
|
||||
{
|
||||
m_PrefixStrings = prefixStr;
|
||||
m_Encoding = TextEncoding.UTF8;
|
||||
_buffer = GC.AllocateUninitializedArray<byte>(count < 1 ? BufferSize : count);
|
||||
}
|
||||
|
||||
public virtual long Position => Index;
|
||||
|
||||
protected virtual int BufferSize => 256;
|
||||
|
||||
public byte[] Buffer => _buffer;
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Resize(int size)
|
||||
{
|
||||
// We shouldn't ever resize to a 0 length buffer. That is dangerous
|
||||
if (size <= 0)
|
||||
{
|
||||
size = BufferSize;
|
||||
}
|
||||
|
||||
if (size < _buffer.Length)
|
||||
{
|
||||
_bytesWritten = size;
|
||||
}
|
||||
|
||||
var newBuffer = GC.AllocateUninitializedArray<byte>(size);
|
||||
_buffer.AsSpan(0, Math.Min(size, _buffer.Length)).CopyTo(newBuffer);
|
||||
_buffer = newBuffer;
|
||||
}
|
||||
|
||||
public virtual void Flush()
|
||||
{
|
||||
// Need to avoid buffer.Length = 2, buffer * 2 is 4, but we need 8 or 16bytes, causing an exception.
|
||||
// The least we need is 16bytes + Index, but we use BufferSize since it should always be big enough for a single
|
||||
// non-dynamic field.
|
||||
Resize(Math.Max(BufferSize, _buffer.Length * 2));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void FlushIfNeeded(int amount)
|
||||
{
|
||||
if (Index + amount > _buffer.Length)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
var remaining = bytes.Length;
|
||||
var idx = 0;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
FlushIfNeeded(remaining);
|
||||
|
||||
var count = Math.Min((int)(_buffer.Length - Index), remaining);
|
||||
bytes.Slice(idx, count).CopyTo(_buffer.AsSpan((int)Index, count));
|
||||
|
||||
idx += count;
|
||||
Index += count;
|
||||
remaining -= count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(BitArray bitArray)
|
||||
{
|
||||
var byteLength = BitArray.GetByteArrayLengthFromBitLength(bitArray.Length);
|
||||
|
||||
((IGenericWriter)this).WriteEncodedInt(bitArray.Length);
|
||||
FlushIfNeeded(byteLength);
|
||||
bitArray.CopyTo(_buffer.AsSpan((int)Index, byteLength));
|
||||
Index += byteLength;
|
||||
}
|
||||
|
||||
public virtual long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.End || offset <= 0 && offset > -_buffer.Length,
|
||||
"Attempting to seek to an invalid position using SeekOrigin.End"
|
||||
);
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.Begin || offset >= 0 && offset < _buffer.Length,
|
||||
"Attempting to seek to an invalid position using SeekOrigin.Begin"
|
||||
);
|
||||
Debug.Assert(
|
||||
origin != SeekOrigin.Current || Index + offset >= 0 && Index + offset < _buffer.Length,
|
||||
"Attempting to seek to an invalid position using SeekOrigin.Current"
|
||||
);
|
||||
|
||||
return Index = Math.Max(0, origin switch
|
||||
{
|
||||
SeekOrigin.Current => Index + offset,
|
||||
SeekOrigin.End => _bytesWritten + offset,
|
||||
_ => offset // Begin
|
||||
});
|
||||
}
|
||||
|
||||
public void Write(string value)
|
||||
{
|
||||
if (m_PrefixStrings)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
Write(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(true);
|
||||
InternalWriteString(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InternalWriteString(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(long value)
|
||||
{
|
||||
FlushIfNeeded(8);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
_buffer[Index++] = (byte)(value >> 32);
|
||||
_buffer[Index++] = (byte)(value >> 40);
|
||||
_buffer[Index++] = (byte)(value >> 48);
|
||||
_buffer[Index++] = (byte)(value >> 56);
|
||||
}
|
||||
|
||||
public void Write(ulong value)
|
||||
{
|
||||
FlushIfNeeded(8);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
_buffer[Index++] = (byte)(value >> 32);
|
||||
_buffer[Index++] = (byte)(value >> 40);
|
||||
_buffer[Index++] = (byte)(value >> 48);
|
||||
_buffer[Index++] = (byte)(value >> 56);
|
||||
}
|
||||
|
||||
public void Write(int value)
|
||||
{
|
||||
FlushIfNeeded(4);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
}
|
||||
|
||||
public void Write(uint value)
|
||||
{
|
||||
FlushIfNeeded(4);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
_buffer[Index++] = (byte)(value >> 16);
|
||||
_buffer[Index++] = (byte)(value >> 24);
|
||||
}
|
||||
|
||||
public void Write(short value)
|
||||
{
|
||||
FlushIfNeeded(2);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
}
|
||||
|
||||
public void Write(ushort value)
|
||||
{
|
||||
FlushIfNeeded(2);
|
||||
|
||||
_buffer[Index++] = (byte)value;
|
||||
_buffer[Index++] = (byte)(value >> 8);
|
||||
}
|
||||
|
||||
public unsafe void Write(double value)
|
||||
{
|
||||
FlushIfNeeded(8);
|
||||
|
||||
fixed (byte* pBuffer = _buffer)
|
||||
{
|
||||
*(double*)(pBuffer + Index) = value;
|
||||
}
|
||||
|
||||
Index += 8;
|
||||
}
|
||||
|
||||
public unsafe void Write(float value)
|
||||
{
|
||||
FlushIfNeeded(4);
|
||||
|
||||
fixed (byte* pBuffer = _buffer)
|
||||
{
|
||||
*(float*)(pBuffer + Index) = value;
|
||||
}
|
||||
|
||||
Index += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(byte value)
|
||||
{
|
||||
FlushIfNeeded(1);
|
||||
_buffer[Index++] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(sbyte value)
|
||||
{
|
||||
FlushIfNeeded(1);
|
||||
_buffer[Index++] = (byte)value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe void Write(bool value)
|
||||
{
|
||||
FlushIfNeeded(1);
|
||||
_buffer[Index++] = *(byte*)&value; // up to 30% faster to dereference the raw value on the stack
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(Serial serial) => Write(serial.Value);
|
||||
|
||||
internal void InternalWriteString(string value)
|
||||
{
|
||||
var remaining = m_Encoding.GetByteCount(value);
|
||||
|
||||
((IGenericWriter)this).WriteEncodedInt(remaining);
|
||||
|
||||
if (remaining == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// It is much faster to encode to stack buffer, then copy to the real buffer
|
||||
Span<byte> span = stackalloc byte[Math.Min(BufferSize, 256)];
|
||||
var maxChars = span.Length / m_Encoding.GetMaxByteCount(1);
|
||||
var charsLeft = value.Length;
|
||||
var current = 0;
|
||||
|
||||
while (charsLeft > 0)
|
||||
{
|
||||
var charCount = Math.Min(charsLeft, maxChars);
|
||||
var bytesWritten = m_Encoding.GetBytes(value.AsSpan(current, charCount), span);
|
||||
remaining -= bytesWritten;
|
||||
charsLeft -= charCount;
|
||||
current += charCount;
|
||||
|
||||
Write(span[..bytesWritten]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: GenericPersistence.cs *
|
||||
* *
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: IGenericReader.cs *
|
||||
* *
|
||||
|
|
@ -18,107 +18,106 @@ using System.IO;
|
|||
using System.Net;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public interface IGenericReader
|
||||
{
|
||||
public interface IGenericReader
|
||||
// Used to determine valid Entity deserialization
|
||||
DateTime LastSerialized { get; init; }
|
||||
|
||||
string ReadString(bool intern = false);
|
||||
long ReadLong();
|
||||
ulong ReadULong();
|
||||
int ReadInt();
|
||||
uint ReadUInt();
|
||||
short ReadShort();
|
||||
ushort ReadUShort();
|
||||
double ReadDouble();
|
||||
float ReadFloat();
|
||||
byte ReadByte();
|
||||
sbyte ReadSByte();
|
||||
bool ReadBool();
|
||||
Serial ReadSerial();
|
||||
|
||||
DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc);
|
||||
TimeSpan ReadTimeSpan() => new(ReadLong());
|
||||
|
||||
DateTime ReadDeltaTime()
|
||||
{
|
||||
// Used to determine valid Entity deserialization
|
||||
DateTime LastSerialized { get; init; }
|
||||
|
||||
string ReadString(bool intern = false);
|
||||
long ReadLong();
|
||||
ulong ReadULong();
|
||||
int ReadInt();
|
||||
uint ReadUInt();
|
||||
short ReadShort();
|
||||
ushort ReadUShort();
|
||||
double ReadDouble();
|
||||
float ReadFloat();
|
||||
byte ReadByte();
|
||||
sbyte ReadSByte();
|
||||
bool ReadBool();
|
||||
Serial ReadSerial();
|
||||
|
||||
DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc);
|
||||
TimeSpan ReadTimeSpan() => new(ReadLong());
|
||||
|
||||
DateTime ReadDeltaTime()
|
||||
return ReadLong() switch
|
||||
{
|
||||
return ReadLong() switch
|
||||
{
|
||||
long.MinValue => DateTime.MinValue,
|
||||
long.MaxValue => DateTime.MaxValue,
|
||||
var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc)
|
||||
};
|
||||
}
|
||||
decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() });
|
||||
int ReadEncodedInt()
|
||||
{
|
||||
int v = 0, shift = 0;
|
||||
byte b;
|
||||
|
||||
do
|
||||
{
|
||||
b = ReadByte();
|
||||
v |= (b & 0x7F) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
while (b >= 0x80);
|
||||
|
||||
return v;
|
||||
}
|
||||
IPAddress ReadIPAddress()
|
||||
{
|
||||
byte length = ReadByte();
|
||||
// Either 2 ushorts, or 8 ushorts
|
||||
Span<byte> integer = stackalloc byte[length];
|
||||
Read(integer);
|
||||
return Utility.Intern(new IPAddress(integer));
|
||||
}
|
||||
Point3D ReadPoint3D() => new(ReadInt(), ReadInt(), ReadInt());
|
||||
Point2D ReadPoint2D() => new(ReadInt(), ReadInt());
|
||||
Rectangle2D ReadRect2D() => new(ReadPoint2D(), ReadPoint2D());
|
||||
Rectangle3D ReadRect3D() => new(ReadPoint3D(), ReadPoint3D());
|
||||
Map ReadMap() => Map.Maps[ReadByte()];
|
||||
Race ReadRace() => Race.Races[ReadByte()];
|
||||
int Read(Span<byte> buffer);
|
||||
unsafe T ReadEnum<T>() where T : unmanaged, Enum
|
||||
{
|
||||
switch (sizeof(T))
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
var num = ReadByte();
|
||||
return *(T*)#
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
var num = ReadShort();
|
||||
return *(T*)#
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
var num = ReadEncodedInt();
|
||||
return *(T*)#
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
var num = ReadLong();
|
||||
return *(T*)#
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
Guid ReadGuid()
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[16];
|
||||
Read(bytes);
|
||||
return new Guid(bytes);
|
||||
}
|
||||
|
||||
BitArray ReadBitArray();
|
||||
|
||||
long Seek(long offset, SeekOrigin origin);
|
||||
long.MinValue => DateTime.MinValue,
|
||||
long.MaxValue => DateTime.MaxValue,
|
||||
var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc)
|
||||
};
|
||||
}
|
||||
decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() });
|
||||
int ReadEncodedInt()
|
||||
{
|
||||
int v = 0, shift = 0;
|
||||
byte b;
|
||||
|
||||
do
|
||||
{
|
||||
b = ReadByte();
|
||||
v |= (b & 0x7F) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
while (b >= 0x80);
|
||||
|
||||
return v;
|
||||
}
|
||||
IPAddress ReadIPAddress()
|
||||
{
|
||||
byte length = ReadByte();
|
||||
// Either 2 ushorts, or 8 ushorts
|
||||
Span<byte> integer = stackalloc byte[length];
|
||||
Read(integer);
|
||||
return Utility.Intern(new IPAddress(integer));
|
||||
}
|
||||
Point3D ReadPoint3D() => new(ReadInt(), ReadInt(), ReadInt());
|
||||
Point2D ReadPoint2D() => new(ReadInt(), ReadInt());
|
||||
Rectangle2D ReadRect2D() => new(ReadPoint2D(), ReadPoint2D());
|
||||
Rectangle3D ReadRect3D() => new(ReadPoint3D(), ReadPoint3D());
|
||||
Map ReadMap() => Map.Maps[ReadByte()];
|
||||
Race ReadRace() => Race.Races[ReadByte()];
|
||||
int Read(Span<byte> buffer);
|
||||
unsafe T ReadEnum<T>() where T : unmanaged, Enum
|
||||
{
|
||||
switch (sizeof(T))
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
var num = ReadByte();
|
||||
return *(T*)#
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
var num = ReadShort();
|
||||
return *(T*)#
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
var num = ReadEncodedInt();
|
||||
return *(T*)#
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
var num = ReadLong();
|
||||
return *(T*)#
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
Guid ReadGuid()
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[16];
|
||||
Read(bytes);
|
||||
return new Guid(bytes);
|
||||
}
|
||||
|
||||
BitArray ReadBitArray();
|
||||
|
||||
long Seek(long offset, SeekOrigin origin);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: IGenericWriter.cs *
|
||||
* *
|
||||
|
|
@ -18,151 +18,150 @@ using System.IO;
|
|||
using System.Net;
|
||||
using Server.Collections;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public interface IGenericWriter
|
||||
{
|
||||
public interface IGenericWriter
|
||||
long Position { get; }
|
||||
void Close();
|
||||
void Write(string value);
|
||||
void Write(long value);
|
||||
void Write(ulong value);
|
||||
void Write(int value);
|
||||
void Write(uint value);
|
||||
void Write(short value);
|
||||
void Write(ushort value);
|
||||
void Write(double value);
|
||||
void Write(float value);
|
||||
void Write(byte value);
|
||||
void Write(sbyte value);
|
||||
void Write(bool value);
|
||||
void Write(Serial serial);
|
||||
|
||||
void Write(DateTime value)
|
||||
{
|
||||
long Position { get; }
|
||||
void Close();
|
||||
void Write(string value);
|
||||
void Write(long value);
|
||||
void Write(ulong value);
|
||||
void Write(int value);
|
||||
void Write(uint value);
|
||||
void Write(short value);
|
||||
void Write(ushort value);
|
||||
void Write(double value);
|
||||
void Write(float value);
|
||||
void Write(byte value);
|
||||
void Write(sbyte value);
|
||||
void Write(bool value);
|
||||
void Write(Serial serial);
|
||||
|
||||
void Write(DateTime value)
|
||||
// If DateTimeKind is Unspecified, we can't assume it needs to be converted.
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
// If DateTimeKind is Unspecified, we can't assume it needs to be converted.
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
value = value.ToUniversalTime();
|
||||
}
|
||||
|
||||
Write(value.Ticks);
|
||||
}
|
||||
void WriteDeltaTime(DateTime value)
|
||||
{
|
||||
if (value == DateTime.MinValue)
|
||||
{
|
||||
Write(long.MinValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == DateTime.MaxValue)
|
||||
{
|
||||
Write(long.MaxValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
value = value.ToUniversalTime();
|
||||
}
|
||||
|
||||
// Technically supports negative deltas for times in the past
|
||||
Write(value.Ticks - DateTime.UtcNow.Ticks);
|
||||
}
|
||||
void Write(IPAddress value)
|
||||
{
|
||||
Span<byte> stack = stackalloc byte[16];
|
||||
value.TryWriteBytes(stack, out var bytesWritten);
|
||||
Write((byte)bytesWritten);
|
||||
Write(stack[..bytesWritten]);
|
||||
}
|
||||
void Write(TimeSpan value)
|
||||
{
|
||||
Write(value.Ticks);
|
||||
value = value.ToUniversalTime();
|
||||
}
|
||||
|
||||
public void Write(decimal value)
|
||||
{
|
||||
var bits = decimal.GetBits(value);
|
||||
|
||||
for (var i = 0; i < 4; ++i)
|
||||
{
|
||||
Write(bits[i]);
|
||||
}
|
||||
}
|
||||
void WriteEncodedInt(int value)
|
||||
{
|
||||
var v = (uint)value;
|
||||
|
||||
while (v >= 0x80)
|
||||
{
|
||||
Write((byte)(v | 0x80));
|
||||
v >>= 7;
|
||||
}
|
||||
|
||||
Write((byte)v);
|
||||
}
|
||||
void Write(Point3D value)
|
||||
{
|
||||
Write(value.m_X);
|
||||
Write(value.m_Y);
|
||||
Write(value.m_Z);
|
||||
}
|
||||
void Write(Point2D value)
|
||||
{
|
||||
Write(value.m_X);
|
||||
Write(value.m_Y);
|
||||
}
|
||||
void Write(Rectangle2D value)
|
||||
{
|
||||
Write(value.Start);
|
||||
Write(value.End);
|
||||
}
|
||||
void Write(Rectangle3D value)
|
||||
{
|
||||
Write(value.Start);
|
||||
Write(value.End);
|
||||
}
|
||||
void Write(Map value) => Write((byte)(value?.MapIndex ?? 0xFF));
|
||||
void Write(Race value) => Write((byte)(value?.RaceIndex ?? 0xFF));
|
||||
void Write(ReadOnlySpan<byte> bytes);
|
||||
unsafe void WriteEnum<T>(T value) where T : unmanaged, Enum
|
||||
{
|
||||
switch (sizeof(T))
|
||||
{
|
||||
default: throw new ArgumentException($"Argument of type {typeof(T)} is not a normal enum");
|
||||
case 1:
|
||||
{
|
||||
Write(*(byte*)&value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Write(*(ushort*)&value);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
WriteEncodedInt(*(int*)&value);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Write(*(ulong*)&value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Write(Guid guid)
|
||||
{
|
||||
Span<byte> stack = stackalloc byte[16];
|
||||
guid.TryWriteBytes(stack);
|
||||
Write(stack);
|
||||
}
|
||||
|
||||
void Write(BitArray bitArray);
|
||||
|
||||
long Seek(long offset, SeekOrigin origin);
|
||||
Write(value.Ticks);
|
||||
}
|
||||
void WriteDeltaTime(DateTime value)
|
||||
{
|
||||
if (value == DateTime.MinValue)
|
||||
{
|
||||
Write(long.MinValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == DateTime.MaxValue)
|
||||
{
|
||||
Write(long.MaxValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.Kind == DateTimeKind.Local)
|
||||
{
|
||||
value = value.ToUniversalTime();
|
||||
}
|
||||
|
||||
// Technically supports negative deltas for times in the past
|
||||
Write(value.Ticks - DateTime.UtcNow.Ticks);
|
||||
}
|
||||
void Write(IPAddress value)
|
||||
{
|
||||
Span<byte> stack = stackalloc byte[16];
|
||||
value.TryWriteBytes(stack, out var bytesWritten);
|
||||
Write((byte)bytesWritten);
|
||||
Write(stack[..bytesWritten]);
|
||||
}
|
||||
void Write(TimeSpan value)
|
||||
{
|
||||
Write(value.Ticks);
|
||||
}
|
||||
|
||||
public void Write(decimal value)
|
||||
{
|
||||
var bits = decimal.GetBits(value);
|
||||
|
||||
for (var i = 0; i < 4; ++i)
|
||||
{
|
||||
Write(bits[i]);
|
||||
}
|
||||
}
|
||||
void WriteEncodedInt(int value)
|
||||
{
|
||||
var v = (uint)value;
|
||||
|
||||
while (v >= 0x80)
|
||||
{
|
||||
Write((byte)(v | 0x80));
|
||||
v >>= 7;
|
||||
}
|
||||
|
||||
Write((byte)v);
|
||||
}
|
||||
void Write(Point3D value)
|
||||
{
|
||||
Write(value.m_X);
|
||||
Write(value.m_Y);
|
||||
Write(value.m_Z);
|
||||
}
|
||||
void Write(Point2D value)
|
||||
{
|
||||
Write(value.m_X);
|
||||
Write(value.m_Y);
|
||||
}
|
||||
void Write(Rectangle2D value)
|
||||
{
|
||||
Write(value.Start);
|
||||
Write(value.End);
|
||||
}
|
||||
void Write(Rectangle3D value)
|
||||
{
|
||||
Write(value.Start);
|
||||
Write(value.End);
|
||||
}
|
||||
void Write(Map value) => Write((byte)(value?.MapIndex ?? 0xFF));
|
||||
void Write(Race value) => Write((byte)(value?.RaceIndex ?? 0xFF));
|
||||
void Write(ReadOnlySpan<byte> bytes);
|
||||
unsafe void WriteEnum<T>(T value) where T : unmanaged, Enum
|
||||
{
|
||||
switch (sizeof(T))
|
||||
{
|
||||
default: throw new ArgumentException($"Argument of type {typeof(T)} is not a normal enum");
|
||||
case 1:
|
||||
{
|
||||
Write(*(byte*)&value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Write(*(ushort*)&value);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
WriteEncodedInt(*(int*)&value);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Write(*(ulong*)&value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Write(Guid guid)
|
||||
{
|
||||
Span<byte> stack = stackalloc byte[16];
|
||||
guid.TryWriteBytes(stack);
|
||||
Write(stack);
|
||||
}
|
||||
|
||||
void Write(BitArray bitArray);
|
||||
|
||||
long Seek(long offset, SeekOrigin origin);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2020 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: ISerializable.cs *
|
||||
* *
|
||||
|
|
@ -16,69 +16,68 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Server
|
||||
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; }
|
||||
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();
|
||||
bool Deleted { get; }
|
||||
|
||||
void SetTypeRef(Type type);
|
||||
|
||||
public void InitializeSaveBuffer(byte[] buffer)
|
||||
{
|
||||
// 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();
|
||||
bool Deleted { get; }
|
||||
|
||||
void SetTypeRef(Type type);
|
||||
|
||||
public void InitializeSaveBuffer(byte[] buffer)
|
||||
SaveBuffer = new BufferWriter(buffer, true);
|
||||
if (World.DirtyTrackingEnabled)
|
||||
{
|
||||
SaveBuffer = new BufferWriter(buffer, true);
|
||||
if (World.DirtyTrackingEnabled)
|
||||
{
|
||||
SavePosition = SaveBuffer.Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
SavePosition = -1;
|
||||
}
|
||||
SavePosition = SaveBuffer.Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
SavePosition = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize()
|
||||
{
|
||||
SaveBuffer ??= new BufferWriter(true);
|
||||
|
||||
BeforeSerialize();
|
||||
|
||||
// Clean, don't bother serializing
|
||||
if (SavePosition > -1)
|
||||
{
|
||||
SaveBuffer.Seek(SavePosition, SeekOrigin.Begin);
|
||||
return;
|
||||
}
|
||||
|
||||
public void Serialize()
|
||||
LastSerialized = Core.Now;
|
||||
SaveBuffer.Seek(0, SeekOrigin.Begin);
|
||||
Serialize(SaveBuffer);
|
||||
|
||||
if (World.DirtyTrackingEnabled)
|
||||
{
|
||||
SaveBuffer ??= new BufferWriter(true);
|
||||
|
||||
BeforeSerialize();
|
||||
|
||||
// Clean, don't bother serializing
|
||||
if (SavePosition > -1)
|
||||
{
|
||||
SaveBuffer.Seek(SavePosition, SeekOrigin.Begin);
|
||||
return;
|
||||
}
|
||||
|
||||
LastSerialized = Core.Now;
|
||||
SaveBuffer.Seek(0, SeekOrigin.Begin);
|
||||
Serialize(SaveBuffer);
|
||||
|
||||
if (World.DirtyTrackingEnabled)
|
||||
{
|
||||
SavePosition = SaveBuffer.Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MarkDirty();
|
||||
}
|
||||
SavePosition = SaveBuffer.Position;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2021 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: ISerializableExtensions.cs *
|
||||
* *
|
||||
|
|
@ -17,165 +17,164 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public static class ISerializableExtensions
|
||||
{
|
||||
public static class ISerializableExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void MarkDirty(this ISerializable entity)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void MarkDirty(this ISerializable entity)
|
||||
if (entity != null)
|
||||
{
|
||||
if (entity != null)
|
||||
{
|
||||
entity.SavePosition = -1;
|
||||
}
|
||||
entity.SavePosition = -1;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Delete(this ISerializable entity, IEntity toDelete)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Delete(this ISerializable entity, IEntity toDelete)
|
||||
{
|
||||
toDelete?.Delete();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<T>(this ISerializable entity, ICollection<T> list, T value)
|
||||
{
|
||||
list.Add(value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<K, V>(this ISerializable entity, IDictionary<K, V> dict, K key, V value)
|
||||
{
|
||||
dict[key] = value;
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Insert<T>(this ISerializable entity, IList<T> list, T value, int index)
|
||||
{
|
||||
list.Insert(index, value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Remove<T>(this ISerializable entity, ICollection<T> list, T value)
|
||||
{
|
||||
if (list.Remove(value))
|
||||
{
|
||||
toDelete?.Delete();
|
||||
entity.MarkDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<T>(this ISerializable entity, ICollection<T> list, T value)
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Remove<K, V>(this ISerializable entity, IDictionary<K, V> dict, K key, out V value)
|
||||
{
|
||||
if (dict.Remove(key, out value))
|
||||
{
|
||||
list.Add(value);
|
||||
entity.MarkDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<K, V>(this ISerializable entity, IDictionary<K, V> dict, K key, V value)
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void RemoveAt<T>(this ISerializable entity, IList<T> list, int index)
|
||||
{
|
||||
list.RemoveAt(index);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<T>(this ISerializable entity, ICollection<T> list)
|
||||
{
|
||||
list.Clear();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Stop(this ISerializable entity, Timer timer)
|
||||
{
|
||||
timer?.Stop();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Start(this ISerializable entity, Timer timer)
|
||||
{
|
||||
timer?.Start();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Restart(this ISerializable entity, Timer timer, TimeSpan delay, TimeSpan interval)
|
||||
{
|
||||
if (timer != null)
|
||||
{
|
||||
dict[key] = value;
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Insert<T>(this ISerializable entity, IList<T> list, T value, int index)
|
||||
{
|
||||
list.Insert(index, value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Remove<T>(this ISerializable entity, ICollection<T> list, T value)
|
||||
{
|
||||
if (list.Remove(value))
|
||||
{
|
||||
entity.MarkDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Remove<K, V>(this ISerializable entity, IDictionary<K, V> dict, K key, out V value)
|
||||
{
|
||||
if (dict.Remove(key, out value))
|
||||
{
|
||||
entity.MarkDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void RemoveAt<T>(this ISerializable entity, IList<T> list, int index)
|
||||
{
|
||||
list.RemoveAt(index);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<T>(this ISerializable entity, ICollection<T> list)
|
||||
{
|
||||
list.Clear();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Stop(this ISerializable entity, Timer timer)
|
||||
{
|
||||
timer?.Stop();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Start(this ISerializable entity, Timer timer)
|
||||
{
|
||||
timer?.Start();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Restart(this ISerializable entity, Timer timer, TimeSpan delay, TimeSpan interval)
|
||||
{
|
||||
if (timer != null)
|
||||
{
|
||||
timer.Stop();
|
||||
timer.Delay = delay;
|
||||
timer.Interval = interval;
|
||||
timer.Start();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<T>(this ISerializable entity, ref List<T> list, T value)
|
||||
{
|
||||
Utility.Add(ref list, value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<K, V>(this ISerializable entity, ref Dictionary<K, V> dict, K key, V value)
|
||||
{
|
||||
Utility.Add(ref dict, key, value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Remove<T>(this ISerializable entity, ref List<T> list, T value)
|
||||
{
|
||||
if (Utility.Remove(ref list, value))
|
||||
{
|
||||
entity.MarkDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<T>(this ISerializable entity, ref List<T> list)
|
||||
{
|
||||
Utility.Clear(ref list);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<T>(this ISerializable entity, ref HashSet<T> set)
|
||||
{
|
||||
Utility.Clear(ref set);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<K, V>(this ISerializable entity, ref Dictionary<K, V> dict)
|
||||
{
|
||||
Utility.Clear(ref dict);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Stop(this ISerializable entity, ref Timer timer)
|
||||
{
|
||||
timer?.Stop();
|
||||
timer = null;
|
||||
timer.Stop();
|
||||
timer.Delay = delay;
|
||||
timer.Interval = interval;
|
||||
timer.Start();
|
||||
entity.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<T>(this ISerializable entity, ref List<T> list, T value)
|
||||
{
|
||||
Utility.Add(ref list, value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add<K, V>(this ISerializable entity, ref Dictionary<K, V> dict, K key, V value)
|
||||
{
|
||||
Utility.Add(ref dict, key, value);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Remove<T>(this ISerializable entity, ref List<T> list, T value)
|
||||
{
|
||||
if (Utility.Remove(ref list, value))
|
||||
{
|
||||
entity.MarkDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<T>(this ISerializable entity, ref List<T> list)
|
||||
{
|
||||
Utility.Clear(ref list);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<T>(this ISerializable entity, ref HashSet<T> set)
|
||||
{
|
||||
Utility.Clear(ref set);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Clear<K, V>(this ISerializable entity, ref Dictionary<K, V> dict)
|
||||
{
|
||||
Utility.Clear(ref dict);
|
||||
entity.MarkDirty();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Stop(this ISerializable entity, ref Timer timer)
|
||||
{
|
||||
timer?.Stop();
|
||||
timer = null;
|
||||
entity.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: Persistence.cs *
|
||||
* *
|
||||
|
|
@ -18,112 +18,111 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Server
|
||||
namespace Server;
|
||||
|
||||
public class Persistence
|
||||
{
|
||||
public class Persistence
|
||||
public const int DefaultPriority = 100;
|
||||
|
||||
private static readonly SortedSet<RegistryEntry> _registry = new(new RegistryEntryComparer());
|
||||
|
||||
public static void Register(
|
||||
string name,
|
||||
Action serializer,
|
||||
Action<string> snapshotWriter,
|
||||
Action<string> deserializer,
|
||||
int priority = DefaultPriority
|
||||
)
|
||||
{
|
||||
public const int DefaultPriority = 100;
|
||||
|
||||
private static readonly SortedSet<RegistryEntry> _registry = new(new RegistryEntryComparer());
|
||||
|
||||
public static void Register(
|
||||
string name,
|
||||
Action serializer,
|
||||
Action<string> snapshotWriter,
|
||||
Action<string> deserializer,
|
||||
int priority = DefaultPriority
|
||||
)
|
||||
{
|
||||
_registry.Add(
|
||||
new RegistryEntry
|
||||
{
|
||||
Name = name,
|
||||
Priority = priority,
|
||||
Serialize = serializer,
|
||||
WriteSnapshot = snapshotWriter,
|
||||
Deserialize = deserializer
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void Unregister(string name) => _registry.RemoveWhere(entry => entry.Name == name);
|
||||
|
||||
public static void Load(string path)
|
||||
{
|
||||
// This should probably not be parallel since Mobiles must be loaded before Items
|
||||
foreach (var entry in _registry)
|
||||
_registry.Add(
|
||||
new RegistryEntry
|
||||
{
|
||||
entry.Deserialize(path);
|
||||
Name = name,
|
||||
Priority = priority,
|
||||
Serialize = serializer,
|
||||
WriteSnapshot = snapshotWriter,
|
||||
Deserialize = deserializer
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void Serialize()
|
||||
public static void Unregister(string name) => _registry.RemoveWhere(entry => entry.Name == name);
|
||||
|
||||
public static void Load(string path)
|
||||
{
|
||||
// This should probably not be parallel since Mobiles must be loaded before Items
|
||||
foreach (var entry in _registry)
|
||||
{
|
||||
Parallel.ForEach(_registry, entry => entry.Serialize());
|
||||
}
|
||||
|
||||
public static void WriteSnapshot(string path)
|
||||
{
|
||||
foreach (var entry in _registry)
|
||||
{
|
||||
entry.WriteSnapshot(path);
|
||||
}
|
||||
}
|
||||
|
||||
public record RegistryEntry
|
||||
{
|
||||
public string Name { get; init; }
|
||||
public int Priority { get; init; }
|
||||
public Action Serialize { get; init; } // Serializing to memory buffers
|
||||
public Action<string> WriteSnapshot { get; init; }
|
||||
public Action<string> Deserialize { get; init; }
|
||||
}
|
||||
|
||||
internal class RegistryEntryComparer : IComparer<RegistryEntry>
|
||||
{
|
||||
public int Compare(RegistryEntry x, RegistryEntry y)
|
||||
{
|
||||
if (x == y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// First sort by priority
|
||||
var cmp = x.Priority.CompareTo(y.Priority);
|
||||
|
||||
// Then alphabetically. We won't allow the same entry (by name) twice in the SortedSet
|
||||
return cmp != 0 ? cmp : x.Name?.CompareOrdinal(y.Name) ?? -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void TraceException(Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var op = new StreamWriter("save-errors.log", true);
|
||||
op.WriteLine("# {0}", Core.Now);
|
||||
|
||||
op.WriteLine(ex);
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
Console.WriteLine(ex);
|
||||
entry.Deserialize(path);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Serialize()
|
||||
{
|
||||
Parallel.ForEach(_registry, entry => entry.Serialize());
|
||||
}
|
||||
|
||||
public static void WriteSnapshot(string path)
|
||||
{
|
||||
foreach (var entry in _registry)
|
||||
{
|
||||
entry.WriteSnapshot(path);
|
||||
}
|
||||
}
|
||||
|
||||
public record RegistryEntry
|
||||
{
|
||||
public string Name { get; init; }
|
||||
public int Priority { get; init; }
|
||||
public Action Serialize { get; init; } // Serializing to memory buffers
|
||||
public Action<string> WriteSnapshot { get; init; }
|
||||
public Action<string> Deserialize { get; init; }
|
||||
}
|
||||
|
||||
internal class RegistryEntryComparer : IComparer<RegistryEntry>
|
||||
{
|
||||
public int Compare(RegistryEntry x, RegistryEntry y)
|
||||
{
|
||||
if (x == y)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (x == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// First sort by priority
|
||||
var cmp = x.Priority.CompareTo(y.Priority);
|
||||
|
||||
// Then alphabetically. We won't allow the same entry (by name) twice in the SortedSet
|
||||
return cmp != 0 ? cmp : x.Name?.CompareOrdinal(y.Name) ?? -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static void TraceException(Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var op = new StreamWriter("save-errors.log", true);
|
||||
op.WriteLine("# {0}", Core.Now);
|
||||
|
||||
op.WriteLine(ex);
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright 2019-2022 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: SerializationExtensions.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Server.Guilds;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue