Fixes bugs in world save (#297)
- [X] Fixes bugs in the buffer writer
- [X] Removes background save commands
- [X] Avoids calling Serialize() on deserialization
- In a future optimization I will copy the entire object to a buffer, deserialize using a SpanReader, then use that buffer for the SaveBuffer
- [X] Fixes issue with decay queue.
Bumps release version
This commit is contained in:
parent
daf48ebdf2
commit
55935d30b4
12 changed files with 122 additions and 153 deletions
|
|
@ -38,10 +38,10 @@ namespace Server.Guilds
|
||||||
|
|
||||||
public int TypeRef => 0;
|
public int TypeRef => 0;
|
||||||
|
|
||||||
public void Serialize(DateTime serializeStart)
|
public void Serialize()
|
||||||
{
|
{
|
||||||
SaveBuffer ??= new BufferWriter(true);
|
SaveBuffer ??= new BufferWriter(true);
|
||||||
SaveBuffer.Flush();
|
SaveBuffer.Reset();
|
||||||
Serialize(SaveBuffer);
|
Serialize(SaveBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ namespace Server
|
||||||
&& p.Y >= Location.m_Y - range
|
&& p.Y >= Location.m_Y - range
|
||||||
&& p.Y <= Location.m_Y + range;
|
&& p.Y <= Location.m_Y + range;
|
||||||
|
|
||||||
public void Serialize(DateTime serializeStart)
|
public void Serialize()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -826,15 +826,10 @@ namespace Server
|
||||||
|
|
||||||
public int TypeRef { get; }
|
public int TypeRef { get; }
|
||||||
|
|
||||||
public void Serialize(DateTime serializeStart)
|
public void Serialize()
|
||||||
{
|
{
|
||||||
if (Decays && Parent == null && Map != Map.Internal && LastMoved + DecayTime <= serializeStart)
|
|
||||||
{
|
|
||||||
World.EnqueueForDecay(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveBuffer ??= new BufferWriter(true);
|
SaveBuffer ??= new BufferWriter(true);
|
||||||
SaveBuffer.Flush();
|
SaveBuffer.Reset();
|
||||||
Serialize(SaveBuffer);
|
Serialize(SaveBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2393,8 +2388,12 @@ namespace Server
|
||||||
|
|
||||||
public bool AtPoint(int x, int y) => m_Location.m_X == x && m_Location.m_Y == y;
|
public bool AtPoint(int x, int y) => m_Location.m_X == x && m_Location.m_Y == y;
|
||||||
|
|
||||||
|
public virtual bool CanDecay() =>
|
||||||
|
Decays && Parent == null && Map != Map.Internal;
|
||||||
|
|
||||||
|
|
||||||
public virtual bool OnDecay() =>
|
public virtual bool OnDecay() =>
|
||||||
Decays && Parent == null && Map != Map.Internal && Region.Find(Location, Map).OnDecay(this);
|
CanDecay() && Region.Find(Location, Map).OnDecay(this);
|
||||||
|
|
||||||
public void SetLastMoved()
|
public void SetLastMoved()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2638,10 +2638,10 @@ namespace Server
|
||||||
|
|
||||||
public int TypeRef { get; }
|
public int TypeRef { get; }
|
||||||
|
|
||||||
public void Serialize(DateTime serializeStart)
|
public void Serialize()
|
||||||
{
|
{
|
||||||
SaveBuffer ??= new BufferWriter(true);
|
SaveBuffer ??= new BufferWriter(true);
|
||||||
SaveBuffer.Flush();
|
SaveBuffer.Reset();
|
||||||
Serialize(SaveBuffer);
|
Serialize(SaveBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,22 +145,17 @@ namespace Server
|
||||||
{
|
{
|
||||||
var count = ReadInt();
|
var count = ReadInt();
|
||||||
|
|
||||||
if (count > 0)
|
var list = new List<T>(count);
|
||||||
|
|
||||||
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
var list = new List<T>(count);
|
if (ReadItem() is T item)
|
||||||
|
|
||||||
for (var i = 0; i < count; ++i)
|
|
||||||
{
|
{
|
||||||
if (ReadItem() is T item)
|
list.Add(item);
|
||||||
{
|
|
||||||
list.Add(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<T>();
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<Item> ReadItemSet() => ReadItemSet<Item>();
|
public HashSet<Item> ReadItemSet() => ReadItemSet<Item>();
|
||||||
|
|
@ -169,22 +164,17 @@ namespace Server
|
||||||
{
|
{
|
||||||
var count = ReadInt();
|
var count = ReadInt();
|
||||||
|
|
||||||
if (count > 0)
|
var set = new HashSet<T>();
|
||||||
|
|
||||||
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
var set = new HashSet<T>();
|
if (ReadItem() is T item)
|
||||||
|
|
||||||
for (var i = 0; i < count; ++i)
|
|
||||||
{
|
{
|
||||||
if (ReadItem() is T item)
|
set.Add(item);
|
||||||
{
|
|
||||||
set.Add(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HashSet<T>();
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Mobile> ReadStrongMobileList() => ReadStrongMobileList<Mobile>();
|
public List<Mobile> ReadStrongMobileList() => ReadStrongMobileList<Mobile>();
|
||||||
|
|
@ -193,22 +183,17 @@ namespace Server
|
||||||
{
|
{
|
||||||
var count = ReadInt();
|
var count = ReadInt();
|
||||||
|
|
||||||
if (count > 0)
|
var list = new List<T>(count);
|
||||||
|
|
||||||
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
var list = new List<T>(count);
|
if (ReadMobile() is T m)
|
||||||
|
|
||||||
for (var i = 0; i < count; ++i)
|
|
||||||
{
|
{
|
||||||
if (ReadMobile() is T m)
|
list.Add(m);
|
||||||
{
|
|
||||||
list.Add(m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<T>();
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<Mobile> ReadMobileSet() => ReadMobileSet<Mobile>();
|
public HashSet<Mobile> ReadMobileSet() => ReadMobileSet<Mobile>();
|
||||||
|
|
@ -216,23 +201,17 @@ namespace Server
|
||||||
public HashSet<T> ReadMobileSet<T>() where T : Mobile
|
public HashSet<T> ReadMobileSet<T>() where T : Mobile
|
||||||
{
|
{
|
||||||
var count = ReadInt();
|
var count = ReadInt();
|
||||||
|
var set = new HashSet<T>();
|
||||||
|
|
||||||
if (count > 0)
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
var set = new HashSet<T>();
|
if (ReadMobile() is T item)
|
||||||
|
|
||||||
for (var i = 0; i < count; ++i)
|
|
||||||
{
|
{
|
||||||
if (ReadMobile() is T item)
|
set.Add(item);
|
||||||
{
|
|
||||||
set.Add(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HashSet<T>();
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BaseGuild> ReadStrongGuildList() => ReadStrongGuildList<BaseGuild>();
|
public List<BaseGuild> ReadStrongGuildList() => ReadStrongGuildList<BaseGuild>();
|
||||||
|
|
@ -240,11 +219,10 @@ namespace Server
|
||||||
public List<T> ReadStrongGuildList<T>() where T : BaseGuild
|
public List<T> ReadStrongGuildList<T>() where T : BaseGuild
|
||||||
{
|
{
|
||||||
var count = ReadInt();
|
var count = ReadInt();
|
||||||
|
var list = new List<T>(count);
|
||||||
|
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
var list = new List<T>(count);
|
|
||||||
|
|
||||||
for (var i = 0; i < count; ++i)
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
if (ReadGuild() is T g)
|
if (ReadGuild() is T g)
|
||||||
|
|
@ -252,11 +230,9 @@ namespace Server
|
||||||
list.Add(g);
|
list.Add(g);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new List<T>();
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<BaseGuild> ReadGuildSet() => ReadGuildSet<BaseGuild>();
|
public HashSet<BaseGuild> ReadGuildSet() => ReadGuildSet<BaseGuild>();
|
||||||
|
|
@ -265,10 +241,10 @@ namespace Server
|
||||||
{
|
{
|
||||||
var count = ReadInt();
|
var count = ReadInt();
|
||||||
|
|
||||||
|
var set = new HashSet<T>();
|
||||||
|
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
var set = new HashSet<T>();
|
|
||||||
|
|
||||||
for (var i = 0; i < count; ++i)
|
for (var i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
if (ReadGuild() is T item)
|
if (ReadGuild() is T item)
|
||||||
|
|
@ -276,17 +252,17 @@ namespace Server
|
||||||
set.Add(item);
|
set.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HashSet<T>();
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Race ReadRace() => Race.Races[ReadByte()];
|
public Race ReadRace() => Race.Races[ReadByte()];
|
||||||
|
|
||||||
public bool End() => m_File.PeekChar() == -1;
|
public bool End() => m_File.PeekChar() == -1;
|
||||||
|
|
||||||
|
public int Read(Span<byte> buffer) => m_File.Read(buffer);
|
||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
m_File.Close();
|
m_File.Close();
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,18 @@ namespace Server
|
||||||
public class BinaryFileWriter : BufferWriter
|
public class BinaryFileWriter : BufferWriter
|
||||||
{
|
{
|
||||||
private readonly Stream m_File;
|
private readonly Stream m_File;
|
||||||
|
private long m_Position;
|
||||||
|
|
||||||
public BinaryFileWriter(string filename, bool prefixStr) : base(prefixStr) =>
|
public BinaryFileWriter(string filename, bool prefixStr) : base(prefixStr) =>
|
||||||
m_File = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
|
m_File = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
|
||||||
public BinaryFileWriter(Stream stream, bool prefixStr) : base(prefixStr) => m_File = stream;
|
public BinaryFileWriter(Stream stream, bool prefixStr) : base(prefixStr)
|
||||||
|
{
|
||||||
|
m_File = stream;
|
||||||
|
m_Position = m_File.Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Position => m_Position + m_Index;
|
||||||
|
|
||||||
|
|
||||||
protected override int BufferSize => 512;
|
protected override int BufferSize => 512;
|
||||||
|
|
@ -35,21 +42,24 @@ namespace Server
|
||||||
{
|
{
|
||||||
m_Position += m_Index;
|
m_Position += m_Index;
|
||||||
|
|
||||||
m_File.Write(m_Buffer, 0, m_Index);
|
m_File.Write(m_Buffer, 0, (int)m_Index);
|
||||||
m_Index = 0;
|
m_Index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
{
|
{
|
||||||
base.Close();
|
if (m_Index > 0)
|
||||||
|
{
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
|
||||||
m_File.Close();
|
m_File.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
{
|
{
|
||||||
m_Position += m_Index;
|
Flush();
|
||||||
m_Index = 0;
|
|
||||||
|
|
||||||
return m_Position = m_File.Seek(offset, origin);
|
return m_Position = m_File.Seek(offset, origin);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,7 @@ namespace Server
|
||||||
private readonly bool m_PrefixStrings;
|
private readonly bool m_PrefixStrings;
|
||||||
|
|
||||||
protected byte[] m_Buffer;
|
protected byte[] m_Buffer;
|
||||||
protected int m_Index;
|
protected long m_Index;
|
||||||
protected long m_Position;
|
|
||||||
|
|
||||||
private readonly char[] m_SingleCharBuffer = new char[1];
|
private readonly char[] m_SingleCharBuffer = new char[1];
|
||||||
|
|
||||||
|
|
@ -40,6 +39,13 @@ namespace Server
|
||||||
|
|
||||||
private int m_MaxBufferChars;
|
private int m_MaxBufferChars;
|
||||||
|
|
||||||
|
public BufferWriter(byte[] buffer, bool prefixStr)
|
||||||
|
{
|
||||||
|
m_PrefixStrings = prefixStr;
|
||||||
|
m_Encoding = Utility.UTF8;
|
||||||
|
m_Buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
public BufferWriter(bool prefixStr)
|
public BufferWriter(bool prefixStr)
|
||||||
{
|
{
|
||||||
m_PrefixStrings = prefixStr;
|
m_PrefixStrings = prefixStr;
|
||||||
|
|
@ -47,29 +53,23 @@ namespace Server
|
||||||
m_Buffer = new byte[BufferSize];
|
m_Buffer = new byte[BufferSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual long Position => m_Index;
|
||||||
|
|
||||||
protected virtual int BufferSize => 256;
|
protected virtual int BufferSize => 256;
|
||||||
|
|
||||||
public byte[] Data => m_Buffer;
|
public byte[] Data => m_Buffer;
|
||||||
|
|
||||||
public long Position
|
|
||||||
{
|
|
||||||
get => m_Position + m_Index;
|
|
||||||
set => Seek(value, value < 0 ? SeekOrigin.End : SeekOrigin.Begin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Close()
|
public virtual void Close()
|
||||||
{
|
{
|
||||||
if (m_Index > 0)
|
|
||||||
{
|
|
||||||
Flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Resize(int size)
|
public void Resize(int size)
|
||||||
{
|
{
|
||||||
Array.Resize(ref m_Buffer, size);
|
var copy = new byte[size];
|
||||||
|
Buffer.BlockCopy(m_Buffer, 0, copy, 0, Math.Min(size, m_Buffer.Length));
|
||||||
|
m_Buffer = copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Flush()
|
public virtual void Flush()
|
||||||
|
|
@ -77,16 +77,19 @@ namespace Server
|
||||||
Resize(m_Buffer.Length * 2);
|
Resize(m_Buffer.Length * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
m_Index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual long Seek(long offset, SeekOrigin origin)
|
public virtual long Seek(long offset, SeekOrigin origin)
|
||||||
{
|
{
|
||||||
Flush();
|
|
||||||
|
|
||||||
return origin switch
|
return origin switch
|
||||||
{
|
{
|
||||||
SeekOrigin.Begin => m_Position = offset,
|
SeekOrigin.Begin => m_Index = offset,
|
||||||
SeekOrigin.Current => m_Position += offset,
|
SeekOrigin.Current => m_Index += offset,
|
||||||
SeekOrigin.End => m_Position = BufferSize - offset,
|
SeekOrigin.End => m_Index = BufferSize - offset,
|
||||||
_ => m_Position
|
_ => m_Index
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,7 +324,7 @@ namespace Server
|
||||||
|
|
||||||
m_SingleCharBuffer[0] = value;
|
m_SingleCharBuffer[0] = value;
|
||||||
|
|
||||||
var byteCount = m_Encoding.GetBytes(m_SingleCharBuffer, 0, 1, m_Buffer, m_Index);
|
var byteCount = m_Encoding.GetBytes(m_SingleCharBuffer, 0, 1, m_Buffer, (int)m_Index);
|
||||||
m_Index += byteCount;
|
m_Index += byteCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,13 +340,13 @@ namespace Server
|
||||||
|
|
||||||
public void Write(byte[] value, int length)
|
public void Write(byte[] value, int length)
|
||||||
{
|
{
|
||||||
int remaining = length;
|
var remaining = length;
|
||||||
int idx = 0;
|
var idx = 0;
|
||||||
|
|
||||||
while (remaining > 0)
|
while (remaining > 0)
|
||||||
{
|
{
|
||||||
int size = Math.Min(m_Buffer.Length - m_Index, remaining);
|
int size = Math.Min(m_Buffer.Length - (int)m_Index, remaining);
|
||||||
Buffer.BlockCopy(value, idx, m_Buffer, m_Index, size);
|
Buffer.BlockCopy(value, idx, m_Buffer, (int)m_Index, size);
|
||||||
// value.Slice(idx).CopyTo(m_Buffer.AsSpan(m_Index, size));
|
// value.Slice(idx).CopyTo(m_Buffer.AsSpan(m_Index, size));
|
||||||
|
|
||||||
remaining -= size;
|
remaining -= size;
|
||||||
|
|
@ -775,7 +778,7 @@ namespace Server
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer.BlockCopy(m_CharacterBuffer, 0, m_Buffer, m_Index, byteLength);
|
Buffer.BlockCopy(m_CharacterBuffer, 0, m_Buffer, (int)m_Index, byteLength);
|
||||||
m_Index += byteLength;
|
m_Index += byteLength;
|
||||||
|
|
||||||
current += charCount;
|
current += charCount;
|
||||||
|
|
@ -791,7 +794,7 @@ namespace Server
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer.BlockCopy(m_CharacterBuffer, 0, m_Buffer, m_Index, byteLength);
|
Buffer.BlockCopy(m_CharacterBuffer, 0, m_Buffer, (int)m_Index, byteLength);
|
||||||
m_Index += byteLength;
|
m_Index += byteLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,6 @@ namespace Server
|
||||||
HashSet<T> ReadGuildSet<T>() where T : BaseGuild;
|
HashSet<T> ReadGuildSet<T>() where T : BaseGuild;
|
||||||
Race ReadRace();
|
Race ReadRace();
|
||||||
bool End();
|
bool End();
|
||||||
|
int Read(Span<byte> buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Server
|
namespace Server
|
||||||
{
|
{
|
||||||
public interface ISerializable
|
public interface ISerializable
|
||||||
|
|
@ -22,7 +20,7 @@ namespace Server
|
||||||
BufferWriter SaveBuffer { get; set; }
|
BufferWriter SaveBuffer { get; set; }
|
||||||
int TypeRef { get; }
|
int TypeRef { get; }
|
||||||
Serial Serial { get; }
|
Serial Serial { get; }
|
||||||
void Serialize(DateTime serializeStart);
|
void Serialize();
|
||||||
void Deserialize(IGenericReader reader);
|
void Deserialize(IGenericReader reader);
|
||||||
void Serialize(IGenericWriter writer);
|
void Serialize(IGenericWriter writer);
|
||||||
void Delete();
|
void Delete();
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ namespace Server
|
||||||
m_DiskWriteHandle.WaitOne();
|
m_DiskWriteHandle.WaitOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnqueueForDecay(Item item)
|
private static void EnqueueForDecay(Item item)
|
||||||
{
|
{
|
||||||
if (WorldState != WorldState.Saving)
|
if (WorldState != WorldState.Saving)
|
||||||
{
|
{
|
||||||
|
|
@ -338,6 +338,11 @@ namespace Server
|
||||||
}
|
}
|
||||||
t.Delete();
|
t.Delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reader.Seek(entry.Position, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
t.SaveBuffer = new BufferWriter(new byte[entry.Length], true);
|
||||||
|
reader.Read(t.SaveBuffer.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Close();
|
reader.Close();
|
||||||
|
|
@ -381,8 +386,6 @@ namespace Server
|
||||||
|
|
||||||
ProcessSafetyQueues();
|
ProcessSafetyQueues();
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
|
|
||||||
foreach (var item in Items.Values)
|
foreach (var item in Items.Values)
|
||||||
{
|
{
|
||||||
if (item.Parent == null)
|
if (item.Parent == null)
|
||||||
|
|
@ -391,7 +394,6 @@ namespace Server
|
||||||
}
|
}
|
||||||
|
|
||||||
item.ClearProperties();
|
item.ClearProperties();
|
||||||
item.Serialize(now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var m in Mobiles.Values)
|
foreach (var m in Mobiles.Values)
|
||||||
|
|
@ -400,12 +402,6 @@ namespace Server
|
||||||
m.UpdateTotals();
|
m.UpdateTotals();
|
||||||
|
|
||||||
m.ClearProperties();
|
m.ClearProperties();
|
||||||
m.Serialize(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var g in Guilds.Values)
|
|
||||||
{
|
|
||||||
g.Serialize(now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch.Stop();
|
watch.Stop();
|
||||||
|
|
@ -466,6 +462,9 @@ namespace Server
|
||||||
|
|
||||||
public static void WriteFiles(object state)
|
public static void WriteFiles(object state)
|
||||||
{
|
{
|
||||||
|
Console.Write("Closing Save Files...");
|
||||||
|
var watch = Stopwatch.StartNew();
|
||||||
|
|
||||||
IIndexInfo<Serial> itemIndexInfo = new EntityTypeIndex("Items");
|
IIndexInfo<Serial> itemIndexInfo = new EntityTypeIndex("Items");
|
||||||
IIndexInfo<Serial> mobileIndexInfo = new EntityTypeIndex("Mobiles");
|
IIndexInfo<Serial> mobileIndexInfo = new EntityTypeIndex("Mobiles");
|
||||||
IIndexInfo<Serial> guildIndexInfo = new EntityTypeIndex("Guilds");
|
IIndexInfo<Serial> guildIndexInfo = new EntityTypeIndex("Guilds");
|
||||||
|
|
@ -474,10 +473,11 @@ namespace Server
|
||||||
WriteEntities(mobileIndexInfo, Mobiles, MobileTypes);
|
WriteEntities(mobileIndexInfo, Mobiles, MobileTypes);
|
||||||
WriteEntities(guildIndexInfo, Guilds, GuildTypes);
|
WriteEntities(guildIndexInfo, Guilds, GuildTypes);
|
||||||
|
|
||||||
if (m_DiskWriteHandle.Set())
|
watch.Stop();
|
||||||
{
|
|
||||||
Console.WriteLine("Closing Save Files.");
|
m_DiskWriteHandle.Set();
|
||||||
}
|
|
||||||
|
Console.WriteLine("done {0:F1} seconds.", watch.Elapsed.TotalSeconds);
|
||||||
|
|
||||||
Timer.DelayCall(FinishWorldSave);
|
Timer.DelayCall(FinishWorldSave);
|
||||||
}
|
}
|
||||||
|
|
@ -525,8 +525,13 @@ namespace Server
|
||||||
|
|
||||||
private static void SaveEntities<T>(IEnumerable<T> list, DateTime serializeStart) where T : class, ISerializable
|
private static void SaveEntities<T>(IEnumerable<T> list, DateTime serializeStart) where T : class, ISerializable
|
||||||
{
|
{
|
||||||
Parallel.ForEach(list, (t, now) => {
|
Parallel.ForEach(list, t => {
|
||||||
t.Serialize(serializeStart);
|
if (t is Item item && item.CanDecay() && item.LastMoved + item.DecayTime <= serializeStart)
|
||||||
|
{
|
||||||
|
EnqueueForDecay(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Serialize();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,13 +556,13 @@ namespace Server
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WaitForWriteCompletion(); // Blocks Save until current disk flush is done.s
|
||||||
|
|
||||||
++_Saves;
|
++_Saves;
|
||||||
|
|
||||||
NetState.FlushAll();
|
NetState.FlushAll();
|
||||||
NetState.Pause();
|
NetState.Pause();
|
||||||
|
|
||||||
WaitForWriteCompletion(); // Blocks Save until current disk flush is done.
|
|
||||||
|
|
||||||
WorldState = WorldState.Saving;
|
WorldState = WorldState.Saving;
|
||||||
|
|
||||||
m_DiskWriteHandle.Reset();
|
m_DiskWriteHandle.Reset();
|
||||||
|
|
@ -593,12 +598,11 @@ namespace Server
|
||||||
|
|
||||||
Console.WriteLine("Save done in {0:F2} seconds.", duration);
|
Console.WriteLine("Save done in {0:F2} seconds.", duration);
|
||||||
|
|
||||||
Broadcast(
|
// Only broadcast if it took at least 150ms
|
||||||
0x35,
|
if (duration >= 0.15)
|
||||||
true,
|
{
|
||||||
"World save complete. The entire process took {0:F2} seconds.",
|
Broadcast(0x35, true, $"World save completed in {duration:F2} seconds.");
|
||||||
duration
|
}
|
||||||
);
|
|
||||||
|
|
||||||
NetState.Resume();
|
NetState.Resume();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,6 @@ namespace Server.Commands
|
||||||
Register("Help", AccessLevel.Player, Help_OnCommand);
|
Register("Help", AccessLevel.Player, Help_OnCommand);
|
||||||
|
|
||||||
Register("Save", AccessLevel.Administrator, Save_OnCommand);
|
Register("Save", AccessLevel.Administrator, Save_OnCommand);
|
||||||
Register("BackgroundSave", AccessLevel.Administrator, BackgroundSave_OnCommand);
|
|
||||||
Register("BGSave", AccessLevel.Administrator, BackgroundSave_OnCommand);
|
|
||||||
Register("SaveBG", AccessLevel.Administrator, BackgroundSave_OnCommand);
|
|
||||||
|
|
||||||
Register("Move", AccessLevel.GameMaster, Move_OnCommand);
|
Register("Move", AccessLevel.GameMaster, Move_OnCommand);
|
||||||
Register("Client", AccessLevel.Counselor, Client_OnCommand);
|
Register("Client", AccessLevel.Counselor, Client_OnCommand);
|
||||||
|
|
@ -482,20 +479,9 @@ namespace Server.Commands
|
||||||
AutoSave.Save();
|
AutoSave.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Usage("BackgroundSave")]
|
private static bool FixMap(ref Map map, ref Point3D loc, Item item) =>
|
||||||
[Aliases("BGSave", "SaveBG")]
|
map != null && map != Map.Internal || item.RootParent is Mobile m &&
|
||||||
[Description("Saves the world, writing to the disk in the background")]
|
FixMap(ref map, ref loc, m);
|
||||||
private static void BackgroundSave_OnCommand(CommandEventArgs e)
|
|
||||||
{
|
|
||||||
AutoSave.Save(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool FixMap(ref Map map, ref Point3D loc, Item item) => map != null && map != Map.Internal ||
|
|
||||||
item.RootParent is Mobile m && FixMap(
|
|
||||||
ref map,
|
|
||||||
ref loc,
|
|
||||||
m
|
|
||||||
);
|
|
||||||
|
|
||||||
private static bool FixMap(ref Map map, ref Point3D loc, Mobile m)
|
private static bool FixMap(ref Map map, ref Point3D loc, Mobile m)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Server.Misc
|
||||||
|
|
||||||
if (m_Warning == TimeSpan.Zero)
|
if (m_Warning == TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
Save(true);
|
Save();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -85,12 +85,7 @@ namespace Server.Misc
|
||||||
|
|
||||||
public static void Save()
|
public static void Save()
|
||||||
{
|
{
|
||||||
Save(false);
|
if (AutoRestart.Restarting || !World.Running)
|
||||||
}
|
|
||||||
|
|
||||||
public static void Save(bool permitBackgroundWrite)
|
|
||||||
{
|
|
||||||
if (AutoRestart.Restarting)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -118,10 +113,7 @@ namespace Server.Misc
|
||||||
|
|
||||||
var root = Path.Combine(Core.BaseDirectory, "Backups/Automatic");
|
var root = Path.Combine(Core.BaseDirectory, "Backups/Automatic");
|
||||||
|
|
||||||
if (!Directory.Exists(root))
|
AssemblyHandler.EnsureDirectory(root);
|
||||||
{
|
|
||||||
Directory.CreateDirectory(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing = Directory.GetDirectories(root);
|
var existing = Directory.GetDirectories(root);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue