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:
Kamron Batman 2022-10-10 21:47:08 -07:00 committed by GitHub
parent 0138d40bda
commit f268d5d4e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
262 changed files with 28527 additions and 28646 deletions

View file

@ -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;

View file

@ -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
{
}
}

View file

@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2021 - ModernUO Development Team *
* Copyright 2019-2022 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: BinaryFileReader.cs *
* *

View file

@ -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();
}

View file

@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Copyright 2019-2022 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: BufferReader.cs *
* *

View file

@ -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]);
}
}
}

View file

@ -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 *
* *

View file

@ -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*)&num;
}
case 2:
{
var num = ReadShort();
return *(T*)&num;
}
case 4:
{
var num = ReadEncodedInt();
return *(T*)&num;
}
case 8:
{
var num = ReadLong();
return *(T*)&num;
}
}
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*)&num;
}
case 2:
{
var num = ReadShort();
return *(T*)&num;
}
case 4:
{
var num = ReadEncodedInt();
return *(T*)&num;
}
case 8:
{
var num = ReadLong();
return *(T*)&num;
}
}
return default;
}
Guid ReadGuid()
{
Span<byte> bytes = stackalloc byte[16];
Read(bytes);
return new Guid(bytes);
}
BitArray ReadBitArray();
long Seek(long offset, SeekOrigin origin);
}

View file

@ -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);
}

View file

@ -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();
}
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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;