diff --git a/Projects/Server/Buffers/CircularBufferWriter.cs b/Projects/Server/Buffers/CircularBufferWriter.cs index 85fed6180..2bcdc2e29 100644 --- a/Projects/Server/Buffers/CircularBufferWriter.cs +++ b/Projects/Server/Buffers/CircularBufferWriter.cs @@ -17,7 +17,6 @@ using System.Buffers.Binary; using System.IO; using System.Runtime.CompilerServices; using System.Text; -using Server.Network; using Server.Text; namespace System.Buffers diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 5adc47fa5..d9459c8f2 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -172,7 +172,6 @@ namespace System.Buffers var remaining = Remaining; int size; - if (isFixedLength) { size = fixedLength * sizeT; @@ -183,13 +182,14 @@ namespace System.Buffers } else { + // In case the remaining is not evenly divisible size = remaining - (remaining & (sizeT - 1)); + int index = _buffer.Slice(Position, size).IndexOfTerminator(sizeT); + size = index < 0 ? size : index; } - int index = _buffer.Slice(Position, size).IndexOfTerminator(sizeT); - - var span = _buffer.Slice(Position, index < 0 ? size : index); - Position += isFixedLength || index < 0 ? size : index + sizeT; + var span = _buffer.Slice(Position, size); + Position += size; return TextEncoding.GetString(span, encoding, safeString); } diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs index 3650e5f0d..32ca394c9 100644 --- a/Projects/Server/Buffers/SpanWriter.cs +++ b/Projects/Server/Buffers/SpanWriter.cs @@ -21,7 +21,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using Microsoft.Toolkit.HighPerformance; -using Server.Network; using Server.Text; namespace System.Buffers diff --git a/Projects/Server/Buffers/ValueStringBuilder.cs b/Projects/Server/Buffers/ValueStringBuilder.cs index 2ff5be58b..1beda97f8 100644 --- a/Projects/Server/Buffers/ValueStringBuilder.cs +++ b/Projects/Server/Buffers/ValueStringBuilder.cs @@ -6,7 +6,6 @@ using System.Buffers; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Server.Network; namespace Server.Buffers { diff --git a/Projects/Server/Serial.cs b/Projects/Server/Serial.cs index 94ae0f2cb..cb92a3e89 100644 --- a/Projects/Server/Serial.cs +++ b/Projects/Server/Serial.cs @@ -1,3 +1,18 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Serial.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 . * + *************************************************************************/ + using System; using System.Runtime.CompilerServices; @@ -12,54 +27,73 @@ namespace Server public uint Value { get; } - public bool IsMobile => Value > 0 && Value < World.ItemOffset; + public bool IsMobile + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Value > 0 && Value < World.ItemOffset; + } - public bool IsItem => Value >= World.ItemOffset && Value < World.MaxItemSerial; + public bool IsItem + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Value >= World.ItemOffset && Value < World.MaxItemSerial; + } - public bool IsValid => Value > 0; + public bool IsValid + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this != MinusOne; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => Value.GetHashCode(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(Serial other) => Value.CompareTo(other.Value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(uint other) => Value.CompareTo(other); - public override bool Equals(object obj) - { - if (obj is Serial serial) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) => + obj switch { - return this == serial; - } - - if (obj is uint u) - { - return Value == u; - } - - return false; - } + Serial serial => this == serial, + uint u => Value == u, + _ => false + }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Serial l, Serial r) => l.Value == r.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Serial l, Serial r) => l.Value != r.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >(Serial l, Serial r) => l.Value > r.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <(Serial l, Serial r) => l.Value < r.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator >=(Serial l, Serial r) => l.Value >= r.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator <=(Serial l, Serial r) => l.Value <= r.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() => $"0x{Value:X8}"; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator uint(Serial a) => a.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Serial(uint a) => new(a); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Serial other) => Value == other.Value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int ToInt32() => (int)Value; } } diff --git a/Projects/Server/Serialization/AdhocPersistence.cs b/Projects/Server/Serialization/AdhocPersistence.cs new file mode 100644 index 000000000..030c18925 --- /dev/null +++ b/Projects/Server/Serialization/AdhocPersistence.cs @@ -0,0 +1,59 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AdhocPersistence.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 . * + *************************************************************************/ + +using System; +using System.IO; + +namespace Server +{ + public static class AdhocPersistence + { + public static void Serialize(string filePath, Action serializer) + { + var fullPath = Path.Combine(Core.BaseDirectory, filePath); + var file = new FileInfo(fullPath); + file.Directory?.Create(); + + using var bin = new BinaryFileWriter(fullPath, true); + serializer(bin); + } + + public static void Deserialize(string filePath, Action deserializer) + { + var fullPath = Path.Combine(Core.BaseDirectory, filePath); + var file = new FileInfo(fullPath); + file.Directory?.Create(); + + if (!file.Exists) + { + return; + } + + try + { + using FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var br = new BinaryFileReader(fs); + deserializer(br); + } + catch (Exception e) + { + Utility.PushColor(ConsoleColor.Red); + Console.WriteLine($"***** Bad deserialize of {file.FullName} *****"); + Console.WriteLine(e.ToString()); + Utility.PopColor(); + } + } + } +} diff --git a/Projects/Server/Serialization/BinaryFileReader.cs b/Projects/Server/Serialization/BinaryFileReader.cs new file mode 100644 index 000000000..2170dda9f --- /dev/null +++ b/Projects/Server/Serialization/BinaryFileReader.cs @@ -0,0 +1,111 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2021 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: BinaryFileReader.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 . * + *************************************************************************/ + +using System; +using System.IO; +using System.Net; + +namespace Server +{ + public class BinaryFileReader : IGenericReader, IDisposable + { + private readonly BinaryReader _reader; + + public BinaryFileReader(BinaryReader br) => _reader = br; + + public BinaryFileReader(Stream stream) => _reader = new BinaryReader(stream); + + public void Close() => _reader.Close(); + + public string ReadString(bool intern = false) + { + var str = _reader.ReadString(); + return intern ? Utility.Intern(str) : str; + } + + public DateTime ReadDateTime() => new(_reader.ReadInt64(), DateTimeKind.Utc); + + public TimeSpan ReadTimeSpan() => new(_reader.ReadInt64()); + + public DateTime ReadDeltaTime() => new(_reader.ReadInt64() + DateTime.UtcNow.Ticks, DateTimeKind.Utc); + + public decimal ReadDecimal() => _reader.ReadDecimal(); + + public long ReadLong() => _reader.ReadInt64(); + + public ulong ReadULong() => _reader.ReadUInt64(); + + public int ReadInt() => _reader.ReadInt32(); + + public uint ReadUInt() => _reader.ReadUInt32(); + + public short ReadShort() => _reader.ReadInt16(); + + public ushort ReadUShort() => _reader.ReadUInt16(); + + public double ReadDouble() => _reader.ReadDouble(); + + public float ReadFloat() => _reader.ReadSingle(); + + public byte ReadByte() => _reader.ReadByte(); + + public sbyte ReadSByte() => _reader.ReadSByte(); + + public bool ReadBool() => _reader.ReadBoolean(); + + public int ReadEncodedInt() + { + int v = 0, shift = 0; + byte b; + + do + { + b = _reader.ReadByte(); + v |= (b & 0x7F) << shift; + shift += 7; + } + while (b >= 0x80); + + return v; + } + + public IPAddress ReadIPAddress() + { + byte length = ReadByte(); + // Either 2 ushorts, or 8 ushorts + Span integer = stackalloc byte[length]; + Read(integer); + return Utility.Intern(new IPAddress(integer)); + } + + public Point3D ReadPoint3D() => new(ReadInt(), ReadInt(), ReadInt()); + + public Point2D ReadPoint2D() => new(ReadInt(), ReadInt()); + + public Rectangle2D ReadRect2D() => new(ReadPoint2D(), ReadPoint2D()); + + public Rectangle3D ReadRect3D() => new(ReadPoint3D(), ReadPoint3D()); + + public Map ReadMap() => Map.Maps[ReadByte()]; + + public Race ReadRace() => Race.Races[ReadByte()]; + + public int Read(Span buffer) => _reader.Read(buffer); + + public long Seek(long offset, SeekOrigin origin) => _reader.BaseStream.Seek(offset, origin); + + public void Dispose() => Close(); + } +} diff --git a/Projects/Server/Serialization/BinaryFileWriter.cs b/Projects/Server/Serialization/BinaryFileWriter.cs index 639c12043..340e9c66b 100644 --- a/Projects/Server/Serialization/BinaryFileWriter.cs +++ b/Projects/Server/Serialization/BinaryFileWriter.cs @@ -66,9 +66,6 @@ namespace Server return _position = _file.Seek(offset, origin); } - public void Dispose() - { - Close(); - } + public void Dispose() => Close(); } } diff --git a/Projects/Server/Serialization/BufferReader.cs b/Projects/Server/Serialization/BufferReader.cs index 8a494c9a0..1d309887a 100644 --- a/Projects/Server/Serialization/BufferReader.cs +++ b/Projects/Server/Serialization/BufferReader.cs @@ -15,14 +15,10 @@ using System; using System.Buffers.Binary; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; -using System.Runtime.CompilerServices; using System.Text; -using Server.Guilds; -using Server.Network; using Server.Text; namespace Server @@ -33,25 +29,23 @@ namespace Server private byte[] _buffer; private int _position; - public int Position => _position; + public long Position => _position; - public byte[] Buffer => _buffer; - - public BufferReader(byte[] buffer) + public BufferReader(byte[] buffer, Encoding encoding = null) { _buffer = buffer; - _encoding = TextEncoding.UTF8; + _encoding = encoding ?? TextEncoding.UTF8; } - public void SwapBuffers(byte[] newBuffer, out byte[] oldBuffer) + public void Reset(byte[] newBuffer, out byte[] oldBuffer) { oldBuffer = _buffer; _buffer = newBuffer; _position = 0; } - [Obsolete("RunUO backward compatible method that should be replaced.")] - public string ReadString() + // Compatible with BinaryReader.ReadString() + public string ReadString(bool intern = false) { if (!ReadBool()) { @@ -59,146 +53,76 @@ namespace Server } var length = ReadEncodedInt(); - return length <= 0 ? "" : ReadString(_encoding, false, length); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1) - { - int sizeT = TextEncoding.GetByteLengthForEncoding(encoding); - - bool isFixedLength = fixedLength > -1; - - var remaining = _buffer.Length - _position; - int size; - - if (isFixedLength) + if (length <= 0) { - size = fixedLength * sizeT; - if (size > remaining) - { - throw new OutOfMemoryException(); - } - } - else - { - size = remaining - (remaining & (sizeT - 1)); + return intern ? Utility.Intern("") : ""; } - var buffer = _buffer.AsSpan(Position, size); - - int index = buffer.IndexOfTerminator(sizeT); - - var span = buffer[..(index < 0 ? size : index)]; - _position += isFixedLength || index < 0 ? size : index + sizeT; - return TextEncoding.GetString(span, encoding, safeString); + var str = TextEncoding.GetString(_buffer.AsSpan(_position, length), _encoding); + _position += length; + return intern ? Utility.Intern(str) : str; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUniSafe(int fixedLength) => ReadString(TextEncoding.UnicodeLE, true, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUniSafe() => ReadString(TextEncoding.UnicodeLE, true); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUni(int fixedLength) => ReadString(TextEncoding.UnicodeLE, false, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadLittleUni() => ReadString(TextEncoding.UnicodeLE); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUniSafe(int fixedLength) => ReadString(TextEncoding.Unicode, true, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUniSafe() => ReadString(TextEncoding.Unicode, true); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUni(int fixedLength) => ReadString(TextEncoding.Unicode, false, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadBigUni() => ReadString(TextEncoding.Unicode); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8Safe(int fixedLength) => ReadString(TextEncoding.UTF8, true, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8Safe() => ReadString(TextEncoding.UTF8, true); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadUTF8() => ReadString(TextEncoding.UTF8); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAsciiSafe(int fixedLength) => ReadString(Encoding.ASCII, true, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAsciiSafe() => ReadString(Encoding.ASCII, true); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAscii(int fixedLength) => ReadString(Encoding.ASCII, false, fixedLength); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadAscii() => ReadString(Encoding.ASCII); - public DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc); public TimeSpan ReadTimeSpan() => new(ReadLong()); public DateTime ReadDeltaTime() => new(ReadLong() + DateTime.UtcNow.Ticks, DateTimeKind.Utc); - public decimal ReadDecimal() => new(new[] { ReadInt(), ReadInt(), ReadInt(), ReadInt() }); + public decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() }); public long ReadLong() { - var v = BinaryPrimitives.ReadInt64LittleEndian(_buffer.AsSpan(Position, 8)); + var v = BinaryPrimitives.ReadInt64LittleEndian(_buffer.AsSpan(_position, 8)); _position += 8; return v; } public ulong ReadULong() { - var v = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.AsSpan(Position, 8)); + var v = BinaryPrimitives.ReadUInt64LittleEndian(_buffer.AsSpan(_position, 8)); _position += 8; return v; } public int ReadInt() { - var v = BinaryPrimitives.ReadInt32LittleEndian(_buffer.AsSpan(Position, 4)); + var v = BinaryPrimitives.ReadInt32LittleEndian(_buffer.AsSpan(_position, 4)); _position += 4; return v; } public uint ReadUInt() { - var v = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.AsSpan(Position, 4)); + var v = BinaryPrimitives.ReadUInt32LittleEndian(_buffer.AsSpan(_position, 4)); _position += 4; return v; } public short ReadShort() { - var v = BinaryPrimitives.ReadInt16LittleEndian(_buffer.AsSpan(Position, 2)); + var v = BinaryPrimitives.ReadInt16LittleEndian(_buffer.AsSpan(_position, 2)); _position += 2; return v; } public ushort ReadUShort() { - var v = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.AsSpan(Position, 2)); + var v = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.AsSpan(_position, 2)); _position += 2; return v; } public double ReadDouble() { - var v = BinaryPrimitives.ReadDoubleLittleEndian(_buffer.AsSpan(Position, 8)); + var v = BinaryPrimitives.ReadDoubleLittleEndian(_buffer.AsSpan(_position, 8)); _position += 8; return v; } public float ReadFloat() { - var v = BinaryPrimitives.ReadSingleLittleEndian(_buffer.AsSpan(Position, 4)); + var v = BinaryPrimitives.ReadSingleLittleEndian(_buffer.AsSpan(_position, 4)); _position += 4; return v; } @@ -230,7 +154,7 @@ namespace Server // Either 2 ushorts, or 8 ushorts Span integer = stackalloc byte[length]; Read(integer); - return new IPAddress(integer); + return Utility.Intern(new IPAddress(integer)); } public Point3D ReadPoint3D() => new(ReadInt(), ReadInt(), ReadInt()); @@ -243,71 +167,22 @@ namespace Server public Map ReadMap() => Map.Maps[ReadByte()]; - public T ReadEntity() where T : class, ISerializable - { - Serial serial = ReadUInt(); - - // Special case for now: - if (typeof(T).IsAssignableTo(typeof(BaseGuild))) - { - return World.FindGuild(serial) as T; - } - - return World.FindEntity(serial) as T; - } - - public List ReadEntityList() where T : class, ISerializable - { - var count = ReadInt(); - - var list = new List(count); - - for (var i = 0; i < count; ++i) - { - var entity = ReadEntity(); - if (entity != null) - { - list.Add(entity); - } - } - - return list; - } - - public HashSet ReadEntitySet() where T : class, ISerializable - { - var count = ReadInt(); - - var set = new HashSet(count); - - for (var i = 0; i < count; ++i) - { - var entity = ReadEntity(); - if (entity != null) - { - set.Add(entity); - } - } - - return set; - } - public Race ReadRace() => Race.Races[ReadByte()]; public int Read(Span buffer) { var length = buffer.Length; - if (length > _buffer.Length - Position) + if (length > _buffer.Length - _position) { throw new OutOfMemoryException(); } - _buffer.AsSpan(Position, length).CopyTo(buffer); + _buffer.AsSpan(_position, length).CopyTo(buffer); _position += length; return length; } - public virtual int Seek(int offset, SeekOrigin origin) + public virtual long Seek(long offset, SeekOrigin origin) { Debug.Assert( origin != SeekOrigin.End || offset <= 0 && offset > -_buffer.Length, @@ -322,12 +197,19 @@ namespace Server "Attempting to seek to an invalid position using SeekOrigin.Current" ); - return _position = Math.Max(0, origin switch + var position = Math.Max(0L, origin switch { SeekOrigin.Current => _position + offset, SeekOrigin.End => _buffer.Length + offset, _ => offset // Begin }); + + if (position > int.MaxValue) + { + throw new ArgumentException($"BufferReader does not support {nameof(offset)} beyond Int32.MaxValue"); + } + + return _position; } } } diff --git a/Projects/Server/Serialization/BufferWriter.cs b/Projects/Server/Serialization/BufferWriter.cs index 33c29cbdb..98c636c54 100644 --- a/Projects/Server/Serialization/BufferWriter.cs +++ b/Projects/Server/Serialization/BufferWriter.cs @@ -14,13 +14,11 @@ *************************************************************************/ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Net; using System.Runtime.CompilerServices; using System.Text; -using Server.Network; using Server.Text; namespace Server @@ -387,35 +385,6 @@ namespace Server Write((byte)(value?.RaceIndex ?? 0xFF)); } - public void Write(ISerializable value) - { - Write(value?.Deleted != false ? Serial.MinusOne : value.Serial); - } - - public void Write(ICollection coll) where T : class, ISerializable - { - Write(coll.Count); - foreach (var entry in coll) - { - Write(entry); - } - } - - public void Write(ICollection coll, Action action) where T : class, ISerializable - { - if (coll == null) - { - Write(0); - return; - } - - Write(coll.Count); - foreach (var entry in coll) - { - action(this, entry); - } - } - internal void InternalWriteString(string value) { var remaining = m_Encoding.GetByteCount(value); diff --git a/Projects/Server/Serialization/GenericPersistence.cs b/Projects/Server/Serialization/GenericPersistence.cs index ec5087403..2c75194a8 100644 --- a/Projects/Server/Serialization/GenericPersistence.cs +++ b/Projects/Server/Serialization/GenericPersistence.cs @@ -53,7 +53,6 @@ namespace Server void Deserialize(string savePath) { var path = Path.Combine(savePath, name); - AssemblyHandler.EnsureDirectory(path); string binPath = Path.Combine(path, $"{name}.bin"); @@ -65,10 +64,9 @@ namespace Server try { - using FileStream bin = new FileStream(binPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var buffer = GC.AllocateUninitializedArray((int)bin.Length); - bin.Read(buffer); - deserializer(new BufferReader(buffer)); + using FileStream fs = new FileStream(binPath, FileMode.Open, FileAccess.Read, FileShare.Read); + using var br = new BinaryFileReader(fs); + deserializer(br); } catch (Exception e) { diff --git a/Projects/Server/Serialization/IGenericReader.cs b/Projects/Server/Serialization/IGenericReader.cs index 6d180ba23..15f71d8ee 100644 --- a/Projects/Server/Serialization/IGenericReader.cs +++ b/Projects/Server/Serialization/IGenericReader.cs @@ -14,14 +14,14 @@ *************************************************************************/ using System; -using System.Collections.Generic; +using System.IO; using System.Net; namespace Server { public interface IGenericReader { - string ReadString(); + string ReadString(bool intern = false); DateTime ReadDateTime(); TimeSpan ReadTimeSpan(); DateTime ReadDeltaTime(); @@ -44,10 +44,8 @@ namespace Server Rectangle2D ReadRect2D(); Rectangle3D ReadRect3D(); Map ReadMap(); - T ReadEntity() where T : class, ISerializable; - List ReadEntityList() where T : class, ISerializable; - HashSet ReadEntitySet() where T : class, ISerializable; Race ReadRace(); int Read(Span buffer); + long Seek(long offset, SeekOrigin origin); } } diff --git a/Projects/Server/Serialization/IGenericWriter.cs b/Projects/Server/Serialization/IGenericWriter.cs index 5476ec84f..a8a88b608 100644 --- a/Projects/Server/Serialization/IGenericWriter.cs +++ b/Projects/Server/Serialization/IGenericWriter.cs @@ -14,7 +14,6 @@ *************************************************************************/ using System; -using System.Collections.Generic; using System.IO; using System.Net; @@ -47,9 +46,7 @@ namespace Server void Write(Rectangle2D value); void Write(Rectangle3D value); void Write(Map value); - void Write(ISerializable value); void Write(Race value); - void Write(ICollection list) where T : class, ISerializable; void Write(ReadOnlySpan bytes); long Seek(long offset, SeekOrigin origin); diff --git a/Projects/Server/Text/StringHelpers.cs b/Projects/Server/Text/StringHelpers.cs index 61eaa1283..315850078 100644 --- a/Projects/Server/Text/StringHelpers.cs +++ b/Projects/Server/Text/StringHelpers.cs @@ -18,7 +18,6 @@ using System.Buffers; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Server.Network; namespace Server { diff --git a/Projects/Server/TileData.cs b/Projects/Server/TileData.cs index 8291c1cea..6c6a6e1f2 100644 --- a/Projects/Server/TileData.cs +++ b/Projects/Server/TileData.cs @@ -16,6 +16,8 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using System.Text; +using Server.Text; namespace Server { @@ -261,14 +263,12 @@ namespace Server Load(); } - private static void Load() + private unsafe static void Load() { var filePath = Core.FindDataFile("tiledata.mul"); using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var buffer = GC.AllocateUninitializedArray((int)fs.Length); - fs.Read(buffer); - var bin = new BufferReader(buffer); + using var bin = new BinaryReader(fs); bool is64BitFlags; const int landLength = 0x4000; @@ -290,39 +290,47 @@ namespace Server itemLength = 0x4000; } + Span buffer = stackalloc byte[20]; + for (var i = 0; i < landLength; i++) { if (is64BitFlags ? i == 1 || i > 0 && (i & 0x1F) == 0 : (i & 0x1F) == 0) { - bin.ReadInt(); // header + bin.ReadInt32(); // header } - var flags = (TileFlag)(is64BitFlags ? bin.ReadULong() : bin.ReadUInt()); - bin.ReadShort(); // skip 2 bytes -- textureID + var flags = (TileFlag)(is64BitFlags ? bin.ReadUInt64() : bin.ReadUInt32()); + bin.ReadInt16(); // skip 2 bytes -- textureID - LandTable[i] = new LandData(bin.ReadAscii(20), flags); + bin.Read(buffer); + var terminator = buffer.IndexOfTerminator(1); + var name = Encoding.ASCII.GetString(buffer[..(terminator < 0 ? buffer.Length : terminator)]); + LandTable[i] = new LandData(Utility.Intern(name), flags); } for (var i = 0; i < itemLength; i++) { if ((i & 0x1F) == 0) { - bin.ReadInt(); // header + bin.ReadInt32(); // header } - var flags = (TileFlag)(is64BitFlags ? bin.ReadULong() : bin.ReadUInt()); + var flags = (TileFlag)(is64BitFlags ? bin.ReadUInt64() : bin.ReadUInt32()); int weight = bin.ReadByte(); int quality = bin.ReadByte(); - int animation = bin.ReadUShort(); + int animation = bin.ReadUInt16(); bin.ReadByte(); int quantity = bin.ReadByte(); - bin.ReadInt(); + bin.ReadInt32(); bin.ReadByte(); int value = bin.ReadByte(); int height = bin.ReadByte(); + bin.Read(buffer); + var terminator = buffer.IndexOfTerminator(1); + var name = Encoding.ASCII.GetString(buffer[..(terminator < 0 ? buffer.Length : terminator)]); ItemTable[i] = new ItemData( - bin.ReadAscii(20), + Utility.Intern(name), flags, weight, quality, diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index fda12ae00..4baae1869 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -10,7 +10,6 @@ using System.Text; using System.Xml; using Microsoft.Toolkit.HighPerformance; using Server.Buffers; -using Server.Network; using Server.Random; namespace Server diff --git a/Projects/Server/World/EntityPersistence.cs b/Projects/Server/World/EntityPersistence.cs index 1a8769cf6..2fe1d090f 100644 --- a/Projects/Server/World/EntityPersistence.cs +++ b/Projects/Server/World/EntityPersistence.cs @@ -126,14 +126,12 @@ namespace Server continue; } - T t; ConstructorInfo ctor = objs.Item1; I indexer = indexInfo.CreateIndex(number); ctorArgs[0] = indexer; - t = ctor.Invoke(ctorArgs) as T; - if (t != null) + if (ctor.Invoke(ctorArgs) is T t) { entities.Add(new EntityIndex(t, typeID, pos, length)); map[indexer] = t; @@ -162,19 +160,18 @@ namespace Server } using FileStream bin = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); - BufferReader br = null; foreach (var entry in entities) { T t = entry.Entity; - var position = bin.Position; + var position = entry.Position; + bin.Seek(position, SeekOrigin.Begin); // Skip this entry if (t == null) { - bin.Seek(entry.Length, SeekOrigin.Current); continue; } @@ -185,7 +182,7 @@ namespace Server } else { - br.SwapBuffers(buffer, out _); + br.Reset(buffer, out _); } bin.Read(buffer.AsSpan()); @@ -221,8 +218,6 @@ namespace Server } t.Delete(); - // Skip this entry - bin.Seek(position + entry.Length, SeekOrigin.Begin); } } } diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index 6b5c00577..51bee196f 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -153,6 +153,7 @@ namespace Server m_DiskWriteHandle.WaitOne(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void EnqueueForDecay(Item item) { if (WorldState != WorldState.Saving) @@ -468,8 +469,6 @@ namespace Server Broadcast(0x35, true, "The world is saving, please wait."); - var now = DateTime.UtcNow; - logger.Information("Saving world"); var watch = Stopwatch.StartNew(); @@ -645,5 +644,85 @@ namespace Server [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void RemoveGuild(BaseGuild guild) => Guilds.Remove(guild.Serial); + + private struct TypeOf { } + public static T ReadEntity(this IGenericReader reader) where T : class, ISerializable + { + Serial serial = reader.ReadUInt(); + + return default(TypeOf) switch + { + TypeOf => FindGuild(serial) as T, + TypeOf => FindEntity(serial) as T, + _ => default + }; + } + + public static List ReadEntityList(this IGenericReader reader) where T : class, ISerializable + { + var count = reader.ReadInt(); + + var list = new List(count); + + for (var i = 0; i < count; ++i) + { + var entity = reader.ReadEntity(); + if (entity != null) + { + list.Add(entity); + } + } + + return list; + } + + public static HashSet ReadEntitySet(this IGenericReader reader) where T : class, ISerializable + { + var count = reader.ReadInt(); + + var set = new HashSet(count); + + for (var i = 0; i < count; ++i) + { + var entity = reader.ReadEntity(); + if (entity != null) + { + set.Add(entity); + } + } + + return set; + } + + public static void Write(this IGenericWriter writer, ISerializable value) + { + writer.Write(value?.Deleted != false ? Serial.MinusOne : value.Serial); + } + + public static void Write(this IGenericWriter writer, ICollection coll) where T : class, ISerializable + { + writer.Write(coll.Count); + foreach (var entry in coll) + { + writer.Write(entry); + } + } + + public static void Write( + this IGenericWriter writer, ICollection coll, Action action + ) where T : class, ISerializable + { + if (coll == null) + { + writer.Write(0); + return; + } + + writer.Write(coll.Count); + foreach (var entry in coll) + { + action(writer, entry); + } + } } }