diff --git a/Directory.Build.props b/Directory.Build.props index 636e59400..c207ac7cd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,14 +52,7 @@ latest - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - 3.3.1 + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 825ce1e4c..2e682976e 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -3,7 +3,7 @@ false - + diff --git a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs new file mode 100644 index 000000000..0aeffe574 --- /dev/null +++ b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs @@ -0,0 +1,63 @@ +using System; +using System.Buffers; +using Xunit; + +namespace Server.Tests +{ + public class SpanWriterTests + { + [Fact] + public unsafe void TestSpanWriterResizes() + { + Span smallStack = stackalloc byte[8]; + using var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + writer.Write(0x1024L); + + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) + { + fixed (byte* stackPtr = smallStack) + { + Assert.True(spanPtr != stackPtr); + } + } + + Assert.True(span.Length > smallStack.Length); + } + + [Fact] + public unsafe void TestSpanWriterOnlyStackAlloc() + { + Span smallStack = stackalloc byte[8]; + using var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) + { + fixed (byte* stackPtr = smallStack) + { + Assert.True(spanPtr == stackPtr); + } + } + + Assert.True(span.Length == smallStack.Length); + AssertThat.Equal(smallStack, stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0x10, 0x24 }); + } + + [Fact] + public void TestSpanWriterNoResizeThrows() + { + Assert.Throws( + () => + { + Span smallStack = stackalloc byte[8]; + using var writer = new SpanWriter(smallStack); + writer.Write(0x1024L); + writer.Write(0x1024L); + } + ); + } + } +} diff --git a/Projects/Server/Buffers/CircularBufferReader.cs b/Projects/Server/Buffers/CircularBufferReader.cs index b4bd8242a..0bd6d5425 100644 --- a/Projects/Server/Buffers/CircularBufferReader.cs +++ b/Projects/Server/Buffers/CircularBufferReader.cs @@ -370,7 +370,7 @@ namespace Server.Network Position = origin switch { SeekOrigin.Begin => offset, - SeekOrigin.End => Length - offset, + SeekOrigin.End => Length + offset, _ => Position + offset // Current }; } diff --git a/Projects/Server/Buffers/CircularBufferWriter.cs b/Projects/Server/Buffers/CircularBufferWriter.cs index 11a8f0874..345a9d8e0 100644 --- a/Projects/Server/Buffers/CircularBufferWriter.cs +++ b/Projects/Server/Buffers/CircularBufferWriter.cs @@ -528,7 +528,7 @@ namespace System.Buffers Position = origin switch { SeekOrigin.Begin => offset, - SeekOrigin.End => Length - offset, + SeekOrigin.End => Length + offset, _ => Position + offset // Current }; } diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 2f18bd794..754b9b350 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -14,6 +14,7 @@ *************************************************************************/ using System.Buffers.Binary; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; @@ -176,12 +177,44 @@ namespace System.Buffers public string ReadAscii() => ReadString(Encoding.ASCII); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Seek(int offset, SeekOrigin origin) => - Position = origin switch + public int Seek(int offset, SeekOrigin origin) + { + Debug.Assert( + origin != SeekOrigin.End || offset <= 0, + "Attempting to seek to a position beyond capacity using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.End || offset >= -_buffer.Length, + "Attempting to seek to a negative position using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Current || Position + offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Current" + ); + + Debug.Assert( + origin != SeekOrigin.Current || Position + offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Current" + ); + + return Position = Math.Max(0, origin switch { - SeekOrigin.Begin => offset, - SeekOrigin.End => Length - offset, - _ => Position + offset // Current - }; + SeekOrigin.Current => Position + offset, + SeekOrigin.End => _buffer.Length + offset, + _ => offset // Begin + }); + } } } diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs index ac88a392b..ae0e3ecd9 100644 --- a/Projects/Server/Buffers/SpanWriter.cs +++ b/Projects/Server/Buffers/SpanWriter.cs @@ -15,8 +15,10 @@ using System.Buffers.Binary; using System.Data; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using Server; @@ -24,122 +26,173 @@ namespace System.Buffers { public ref struct SpanWriter { - private readonly Span _buffer; + private readonly bool _resize; + private byte[]? _arrayToReturnToPool; + private Span _buffer; + private int _position; - public int Length => _buffer.Length; + public int Length { get; private set; } + + public int Position + { + get => _position; + private set + { + _position = value; + + if (value > Length) + { + Length = value; + } + } + } + + public int Capacity => _buffer.Length; public ReadOnlySpan Span => _buffer.Slice(0, Position); - public int Position { get; private set; } + public Span RawBuffer => _buffer; - public SpanWriter(Span span) + public SpanWriter(Span initialBuffer, bool resize = false) { - _buffer = span; - Position = 0; + _resize = resize; + _buffer = initialBuffer; + _position = 0; + Length = 0; + _arrayToReturnToPool = null; + } + + public SpanWriter(int initialCapacity, bool resize = false) + { + _resize = resize; + _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity); + _buffer = _arrayToReturnToPool; + _position = 0; + Length = 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacity) + { + var newSize = Math.Max(Length + additionalCapacity, _buffer.Length * 2); + byte[] poolArray = ArrayPool.Shared.Rent(newSize); + + _buffer.Slice(0, Length).CopyTo(poolArray); + + byte[]? toReturn = _arrayToReturnToPool; + _buffer = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void GrowIfNeeded(int count) + { + if (_position + count > _buffer.Length) + { + if (!_resize) + { + throw new OutOfMemoryException(); + } + + Grow(count); + } + } + + public ref byte GetPinnableReference() => ref MemoryMarshal.GetReference(_buffer); + + public void EnsureCapacity(int capacity) + { + if (capacity > _buffer.Length) + { + if (!_resize) + { + throw new OutOfMemoryException(); + } + + Grow(capacity - Length); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void Write(bool value) { - if (Position >= Length) - { - throw new OutOfMemoryException(); - } - - _buffer[Position++] = *(byte*) & value; + GrowIfNeeded(1); + _buffer[Position++] = *(byte*)&value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(byte value) { + GrowIfNeeded(1); _buffer[Position++] = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(sbyte value) { + GrowIfNeeded(1); _buffer[Position++] = (byte)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(short value) { - if (!BinaryPrimitives.TryWriteInt16BigEndian(_buffer.Slice(Position), value)) - { - throw new OutOfMemoryException(); - } - + GrowIfNeeded(2); + BinaryPrimitives.WriteInt16BigEndian(_buffer.Slice(_position), value); Position += 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ushort value) { - if (!BinaryPrimitives.TryWriteUInt16BigEndian(_buffer.Slice(Position), value)) - { - throw new OutOfMemoryException(); - } - + GrowIfNeeded(2); + BinaryPrimitives.WriteUInt16BigEndian(_buffer.Slice(_position), value); Position += 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(int value) { - if (!BinaryPrimitives.TryWriteInt32BigEndian(_buffer.Slice(Position), value)) - { - throw new OutOfMemoryException(); - } - + GrowIfNeeded(4); + BinaryPrimitives.WriteInt32BigEndian(_buffer.Slice(_position), value); Position += 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(uint value) { - if (!BinaryPrimitives.TryWriteUInt32BigEndian(_buffer.Slice(Position), value)) - { - throw new OutOfMemoryException(); - } - + GrowIfNeeded(4); + BinaryPrimitives.WriteUInt32BigEndian(_buffer.Slice(_position), value); Position += 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(long value) { - if (!BinaryPrimitives.TryWriteInt64BigEndian(_buffer.Slice(Position), value)) - { - throw new OutOfMemoryException(); - } - + GrowIfNeeded(8); + BinaryPrimitives.WriteInt64BigEndian(_buffer.Slice(_position), value); Position += 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(ulong value) { - if (!BinaryPrimitives.TryWriteUInt64BigEndian(_buffer.Slice(Position), value)) - { - throw new OutOfMemoryException(); - } - + GrowIfNeeded(8); + BinaryPrimitives.WriteUInt64BigEndian(_buffer.Slice(_position), value); Position += 8; } public void Write(ReadOnlySpan buffer) { - var size = buffer.Length; - if (Position + size > Length) - { - throw new OutOfMemoryException(); - } - - buffer.Slice(0, size).CopyTo(_buffer.Slice(Position)); - Position += size; + var count = buffer.Length; + GrowIfNeeded(count); + buffer.CopyTo(_buffer.Slice(_position)); + Position += count; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteString(string value, Encoding encoding, int fixedLength = -1) where T : struct, IEquatable { int sizeT = Unsafe.SizeOf(); @@ -156,12 +209,9 @@ namespace System.Buffers var byteCount = fixedLength > -1 ? fixedLength * sizeT : encoding.GetByteCount(value); - if (Position + byteCount > Length) - { - throw new OutOfMemoryException(); - } + GrowIfNeeded(byteCount); - var bytesWritten = encoding.GetBytes(src, _buffer.Slice(Position)); + var bytesWritten = encoding.GetBytes(src, _buffer.Slice(_position)); Position += bytesWritten; if (fixedLength > -1) @@ -194,7 +244,7 @@ namespace System.Buffers public void WriteBigUniNull(string value) { WriteString(value, Utility.Unicode); - Write((ushort)0); + Write((ushort)0); // '\0' } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -207,7 +257,7 @@ namespace System.Buffers public void WriteUTF8Null(string value) { WriteString(value, Utility.UTF8); - Write((byte)0); + Write((byte)0); // '\0' } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -217,38 +267,77 @@ namespace System.Buffers public void WriteAsciiNull(string value) { WriteString(value, Encoding.ASCII); - Write((byte)0); + Write((byte)0); // '\0' } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteAscii(string value, int fixedLength) => WriteString(value, Encoding.ASCII, fixedLength); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() + public void Clear(int count) { - _buffer.Slice(Position).Clear(); - Position = Length; + GrowIfNeeded(count); + _buffer.Slice(_position, count).Clear(); + Position += count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear(int amount) + public int Seek(int offset, SeekOrigin origin) { - if (Position + amount > Length) + Debug.Assert( + origin != SeekOrigin.End || _resize || offset <= 0, + "Attempting to seek to a position beyond capacity using SeekOrigin.End without resize" + ); + + Debug.Assert( + origin != SeekOrigin.End || offset >= -_buffer.Length, + "Attempting to seek to a negative position using SeekOrigin.End" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Begin" + ); + + Debug.Assert( + origin != SeekOrigin.Begin || _resize || offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin without resize" + ); + + Debug.Assert( + origin != SeekOrigin.Current || _position + offset >= 0, + "Attempting to seek to a negative position using SeekOrigin.Current" + ); + + Debug.Assert( + origin != SeekOrigin.Current || _resize || _position + offset <= _buffer.Length, + "Attempting to seek to a position beyond the capacity using SeekOrigin.Current without resize" + ); + + var newPosition = Math.Max(0, origin switch { - throw new OutOfMemoryException(); + SeekOrigin.Current => _position + offset, + SeekOrigin.End => Length + offset, + _ => offset // Begin + }); + + if (newPosition >= _buffer.Length) + { + Grow(newPosition - _buffer.Length + 1); } - _buffer.Slice(Position, amount).Clear(); - Position += amount; + return _position = newPosition; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Seek(int offset, SeekOrigin origin) => - Position = origin switch + public void Dispose() + { + byte[] toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) { - SeekOrigin.Begin => offset, - SeekOrigin.End => Length - offset, - _ => Position + offset // Current - }; + ArrayPool.Shared.Return(toReturn); + } + } } } diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index aec2110e5..d2717dbd0 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -3,7 +3,7 @@ false - + diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/War Declaration gump.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs similarity index 99% rename from Projects/UOContent/Gumps/Guilds/New Guild System/War Declaration gump.cs rename to Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs index 20ebcc85c..04a992aef 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/War Declaration gump.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/WarDeclarationGump.cs @@ -45,7 +45,7 @@ namespace Server.Guilds return; } - var playerRank = pm.GuildRank; + var playerRank = pm!.GuildRank; switch (info.ButtonID) { diff --git a/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs b/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs index 61ec8efce..9666e247b 100644 --- a/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs +++ b/Projects/UOContent/Gumps/Props/SetPoint2DGump.cs @@ -69,7 +69,7 @@ namespace Server.Gumps m_Page = page; m_List = list; - var p = (Point2D)prop.GetValue(o, null); + var p = (Point2D)(prop?.GetValue(o, null) ?? new Point2D()); AddPage(0); @@ -86,7 +86,7 @@ namespace Server.Gumps var y = BorderSize + OffsetSize; AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); - AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop?.Name); x += EntryWidth + OffsetSize; if (SetGumpID != 0) diff --git a/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs b/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs index 8ab7733d3..0b721b478 100644 --- a/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs +++ b/Projects/UOContent/Gumps/Props/SetPoint3DGump.cs @@ -69,7 +69,7 @@ namespace Server.Gumps m_Page = page; m_List = list; - var p = (Point3D)prop.GetValue(o, null); + var p = (Point3D)(prop?.GetValue(o, null) ?? new Point3D()); AddPage(0); @@ -86,7 +86,7 @@ namespace Server.Gumps var y = BorderSize + OffsetSize; AddImageTiled(x, y, EntryWidth, EntryHeight, EntryGumpID); - AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name); + AddLabelCropped(x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop?.Name); x += EntryWidth + OffsetSize; if (SetGumpID != 0) diff --git a/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs b/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs index 148a29f58..41dc268c7 100644 --- a/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs +++ b/Projects/UOContent/Gumps/Props/SetTimeSpanGump.cs @@ -68,7 +68,7 @@ namespace Server.Gumps m_Page = page; m_List = list; - var ts = (TimeSpan)prop.GetValue(o, null); + var ts = (TimeSpan)(prop?.GetValue(o, null) ?? new TimeSpan()); AddPage(0); @@ -81,7 +81,7 @@ namespace Server.Gumps OffsetGumpID ); - AddRect(0, prop.Name, 0, -1); + AddRect(0, prop?.Name, 0, -1); AddRect(1, ts.ToString(), 0, -1); AddRect(2, "Zero", 1, -1); AddRect(3, "From H:M:S", 2, -1); diff --git a/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs index 7a5b0fda0..92c1d2ed2 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs @@ -29,24 +29,22 @@ namespace Server.Items public override void OnDoubleClick(Mobile from) { - var pm = from as PlayerMobile; - if (!IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - else if (pm == null || from.Skills.Alchemy.Base < 100.0) + else if (from is not PlayerMobile pm || from.Skills.Alchemy.Base < 100.0) { - pm.SendMessage("Only a Grandmaster Alchemist can learn from this book."); + from.SendMessage("Only a Grandmaster Alchemist can learn from this book."); } else if (pm.Glassblowing) { - pm.SendMessage("You have already learned this information."); + from.SendMessage("You have already learned this information."); } else { pm.Glassblowing = true; - pm.SendMessage( + from.SendMessage( "You have learned to make items from glass. You will need to find miners to mine find sand for you to make these items." ); Delete(); diff --git a/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs index ee337d1c8..a97f1b5a1 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs @@ -29,24 +29,22 @@ namespace Server.Items public override void OnDoubleClick(Mobile from) { - var pm = from as PlayerMobile; - if (!IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - else if (pm == null || from.Skills.Carpentry.Base < 100.0) + else if (from is not PlayerMobile pm || from.Skills.Carpentry.Base < 100.0) { - pm.SendMessage("Only a Grandmaster Carpenter can learn from this book."); + from.SendMessage("Only a Grandmaster Carpenter can learn from this book."); } else if (pm.Masonry) { - pm.SendMessage("You have already learned this information."); + from.SendMessage("You have already learned this information."); } else { pm.Masonry = true; - pm.SendMessage( + from.SendMessage( "You have learned to make items from stone. You will need miners to gather stones for you to make these items." ); Delete(); diff --git a/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs index 04099b6a3..dd2f6b773 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs @@ -29,24 +29,22 @@ namespace Server.Items public override void OnDoubleClick(Mobile from) { - var pm = from as PlayerMobile; - if (!IsChildOf(from.Backpack)) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - else if (pm == null || from.Skills.Mining.Base < 100.0) + else if (from is not PlayerMobile pm || from.Skills.Mining.Base < 100.0) { - pm.SendMessage("Only a Grandmaster Miner can learn from this book."); + from.SendMessage("Only a Grandmaster Miner can learn from this book."); } else if (pm.SandMining) { - pm.SendMessage("You have already learned this information."); + from.SendMessage("You have already learned this information."); } else { pm.SandMining = true; - pm.SendMessage( + from.SendMessage( "You have learned how to mine fine sand. Target sand areas when mining to look for fine sand." ); Delete(); diff --git a/Projects/UOContent/Items/Special/SoulStone.cs b/Projects/UOContent/Items/Special/SoulStone.cs index 3035fa8e3..3064645ad 100644 --- a/Projects/UOContent/Items/Special/SoulStone.cs +++ b/Projects/UOContent/Items/Special/SoulStone.cs @@ -156,10 +156,6 @@ namespace Server.Items protected virtual bool CheckUse(Mobile from) { - // DateTime now = DateTime.UtcNow; - - var pm = from as PlayerMobile; - if (Deleted || !IsAccessibleTo(from)) { return false; @@ -235,11 +231,10 @@ namespace Server.Items return false; } - if (pm.AcceleratedStart > DateTime.UtcNow) + if ((from as PlayerMobile)?.AcceleratedStart > DateTime.UtcNow) { - from.SendLocalizedMessage( - 1078115 - ); // You may not use a soulstone while your character is under the effects of a Scroll of Alacrity. + // You may not use a soulstone while your character is under the effects of a Scroll of Alacrity. + from.SendLocalizedMessage(1078115); return false; } @@ -748,8 +743,7 @@ namespace Server.Items return; } - var pm = from as PlayerMobile; - if (pm.AcceleratedStart > DateTime.UtcNow) + if ((from as PlayerMobile)?.AcceleratedStart > DateTime.UtcNow) { //
Unable to Absorb Selected Skill from Soulstone
diff --git a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs index 713f55f93..eeba762e7 100644 --- a/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs +++ b/Projects/UOContent/Mobiles/Monsters/ML/Humanoid/Magic/MLDryad.cs @@ -103,14 +103,12 @@ namespace Server.Mobiles foreach (var m in GetMobilesInRange(RangePerception)) { - var p = m as PlayerMobile; - - if (IsValidTarget(p)) + if (m is PlayerMobile pm && IsValidTarget(pm)) { - p.PeacedUntil = DateTime.UtcNow + duration; - p.SendLocalizedMessage(1072065); // You gaze upon the dryad's beauty, and forget to continue battling! - p.FixedParticles(0x376A, 1, 20, 0x7F5, EffectLayer.Waist); - p.Combatant = null; + pm.PeacedUntil = DateTime.UtcNow + duration; + m.SendLocalizedMessage(1072065); // You gaze upon the dryad's beauty, and forget to continue battling! + m.FixedParticles(0x376A, 1, 20, 0x7F5, EffectLayer.Waist); + m.Combatant = null; } } diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index e88d49329..5b3d08d91 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -28,7 +28,7 @@ false - +