diff --git a/Projects/Server/AssemblyHandler.cs b/Projects/Server/AssemblyHandler.cs index 2a051935f..f8087816c 100644 --- a/Projects/Server/AssemblyHandler.cs +++ b/Projects/Server/AssemblyHandler.cs @@ -14,11 +14,11 @@ *************************************************************************/ using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.Loader; namespace Server @@ -104,7 +104,7 @@ namespace Server for (var i = 0; i < Assemblies.Length; i++) { - foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase)) + foreach (var type in GetTypeCache(Assemblies[i]).GetTypesByName(name, ignoreCase)) { if (type.FullName.EqualsOrdinal(name)) { @@ -113,7 +113,7 @@ namespace Server } } - foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase)) + foreach(var type in GetTypeCache(Core.Assembly).GetTypesByName(name, ignoreCase)) { if (type.FullName.EqualsOrdinal(name)) { @@ -138,13 +138,13 @@ namespace Server for (var i = 0; i < Assemblies.Length; i++) { - foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase)) + foreach (var type in GetTypeCache(Assemblies[i]).GetTypesByName(name, ignoreCase)) { return type; } } - foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase)) + foreach(var type in GetTypeCache(Core.Assembly).GetTypesByName(name, ignoreCase)) { return type; } @@ -164,7 +164,7 @@ namespace Server for (var i = 0; i < Assemblies.Length; i++) { - foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase)) + foreach (var type in GetTypeCache(Assemblies[i]).GetTypesByName(name, ignoreCase)) { if (type.FullName.EqualsOrdinal(name)) { @@ -173,7 +173,7 @@ namespace Server } } - foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase)) + foreach(var type in GetTypeCache(Core.Assembly).GetTypesByName(name, ignoreCase)) { if (type.FullName.EqualsOrdinal(name)) { @@ -196,13 +196,13 @@ namespace Server for (var i = 0; i < Assemblies.Length; i++) { - foreach (var type in GetTypeCache(Assemblies[i]).GetEnumerator(name, ignoreCase)) + foreach (var type in GetTypeCache(Assemblies[i]).GetTypesByName(name, ignoreCase)) { types.Add(type); } } - foreach(var type in GetTypeCache(Core.Assembly).GetEnumerator(name, ignoreCase)) + foreach(var type in GetTypeCache(Core.Assembly).GetTypesByName(name, ignoreCase)) { types.Add(type); } @@ -278,18 +278,38 @@ namespace Server } } - public Enumerator GetEnumerator(string name, bool ignoreCase) => new(name, this, ignoreCase); - public Type[] Types { get; } - public struct Enumerator : IEnumerable, IEnumerator + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TypeEnumerable GetTypesByName(string name, bool ignoreCase) => new(name, this, ignoreCase); + + public ref struct TypeEnumerable + { + private readonly TypeCache _cache; + private readonly string _name; + private readonly bool _ignoreCase; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TypeEnumerable(string name, TypeCache cache, bool ignoreCase) + { + _name = name; + _cache = cache; + _ignoreCase = ignoreCase; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TypeEnumerator GetEnumerator() => new(_name, _cache, _ignoreCase); + } + + public ref struct TypeEnumerator { private readonly TypeCache _cache; private readonly int[] _values; private int _index; private Type _current; - internal Enumerator(string name, TypeCache cache, bool ignoreCase) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal TypeEnumerator(string name, TypeCache cache, bool ignoreCase) { _cache = cache; @@ -299,51 +319,26 @@ namespace Server _current = default; } - public void Dispose() - { - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { int[] localList = _values; - while ((uint)_index < (uint)localList.Length) + if ((uint)_index < (uint)localList.Length) { _current = _cache.Types[_values[_index++]]; - if (_current != null) - { - return true; - } + return true; } return false; } - public Type Current => _current!; - - object IEnumerator.Current + public Type Current { - get - { - if (_index == 0 || _index == _values.Length + 1) - { - throw new InvalidOperationException(nameof(_index)); - } - - return Current; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; } - - void IEnumerator.Reset() - { - _index = 0; - _current = default; - } - - public IEnumerator GetEnumerator() => this; - - IEnumerator IEnumerable.GetEnumerator() => this; } } } diff --git a/Projects/Server/Items/BaseMulti.cs b/Projects/Server/Items/BaseMulti.cs index 1927ffc28..5deb97814 100644 --- a/Projects/Server/Items/BaseMulti.cs +++ b/Projects/Server/Items/BaseMulti.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; namespace Server.Items { @@ -94,8 +95,10 @@ namespace Server.Items && mcl.Tiles[x][y].Length > 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(Mobile m) => m.Map == Map && Contains(m.X, m.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(Item item) => item.Map == Map && Contains(item.X, item.Y); public override void Serialize(IGenericWriter writer) diff --git a/Projects/Server/Skills.cs b/Projects/Server/Skills.cs index 4de41edc4..c685f0765 100644 --- a/Projects/Server/Skills.cs +++ b/Projects/Server/Skills.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Network; namespace Server @@ -491,7 +492,7 @@ namespace Server } [PropertyObject] - public class Skills : IEnumerable + public class Skills { private readonly Skill[] m_Skills; private Skill m_Highest; @@ -805,10 +806,6 @@ namespace Server [CommandProperty(AccessLevel.Counselor)] public Skill Throwing => this[SkillName.Throwing]; - public Enumerator GetEnumerator() => new(m_Skills); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public override string ToString() => "..."; public static bool UseSkill(Mobile from, SkillName name) => UseSkill(from, (int)name); @@ -896,23 +893,24 @@ namespace Server Owner.NetState.SendSkillChange(skill); } - public struct Enumerator : IEnumerator + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SkillsEnumerator GetEnumerator() => new(m_Skills); + + public ref struct SkillsEnumerator { private readonly Skill[] _skills; private int _index; private Skill _current; - internal Enumerator(Skill[] skills) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal SkillsEnumerator(Skill[] skills) { _skills = skills; _index = 0; _current = default; } - public void Dispose() - { - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { Skill[] localList = _skills; @@ -929,25 +927,10 @@ namespace Server return false; } - public Skill Current => _current!; - - object IEnumerator.Current + public Skill Current { - get - { - if (_index == 0 || _index == _skills.Length + 1) - { - throw new InvalidOperationException(nameof(_index)); - } - - return Current; - } - } - - void IEnumerator.Reset() - { - _index = 0; - _current = default; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; } } } diff --git a/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs b/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs index 01b88dde5..dd92d17ee 100644 --- a/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs +++ b/Projects/UOContent.Tests/Tests/Multis/Boats/BoatPacketTests.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; using Moq; using Server; -using Server.Items; using Server.Multis; using Server.Multis.Boats; using Server.Network; @@ -18,33 +18,45 @@ namespace UOContent.Tests [InlineData(Direction.West, 10, 100, 200)] public void TestMoveBoatHS(Direction d, int speed, int xOffset, int yOffset) { - var boat = new Mock((Serial)0x2); - boat.Object.Location = new Point3D(10, 20, 15); - boat.Object.Facing = Direction.Right; + var item1 = new Item((Serial)0x1000) + { + ItemID = 0x13B9, + Location = new Point3D(11, 21, 16), + Map = Map.Felucca, + Visible = true + }; + var item2 = new Item((Serial)0x2000) + { + ItemID = 0x13B9, + Location = new Point3D(100, 200, 16), + Map = Map.Felucca, + Visible = true + }; - // Item on the boat - var item1 = new Mock((Serial)0x2); - item1.Setup(m => m.ItemID).Returns(0x13B9); - item1.Object.Location = new Point3D(10, 20, 15); - - // Item not on the boat - var item2 = new Mock((Serial)0x2); - item2.Setup(m => m.ItemID).Returns(0x13B9); - item2.Object.Location = new Point3D(100, 200, 15); - - var beholder = new Mock((Serial)0x1024u); + var beholder = new Mock((Serial)0x100); + beholder.Object.DefaultMobileInit(); beholder.Object.Location = new Point3D(10, 20, 15); - beholder.Setup(m => m.CanSee(It.Is(e => e == boat.Object))).Returns(true); - beholder.Setup(m => m.CanSee(It.Is(e => e == item1.Object))).Returns(true); - beholder.Setup(m => m.CanSee(It.Is(e => e == item2.Object))).Returns(false); + beholder.Object.Map = Map.Felucca; + beholder.Setup(m => m.CanSee(It.Is(e => e == beholder.Object))).Returns(true); + beholder.Setup(m => m.CanSee(It.Is(e => e == item1))).Returns(true); + beholder.Setup(m => m.CanSee(It.Is(e => e == item2))).Returns(false); - var list = new List(4) { item1.Object, item2.Object, boat.Object, beholder.Object }; + var list = new List { item1, beholder.Object }; + var notContained = new List { item2 }; + var boat = new TestBoat(0x3000, list, notContained) + { + Location = new Point3D(10, 20, 15), + Facing = Direction.Right, + Map = Map.Felucca + }; + + beholder.Setup(m => m.CanSee(It.Is(e => e == boat))).Returns(true); using var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = ProtocolChanges.HighSeas; - var expected = new MoveBoatHS(beholder.Object, boat.Object, d, speed, list, xOffset, yOffset).Compile(); + var expected = new MoveBoatHS(beholder.Object, boat, d, speed, list, xOffset, yOffset).Compile(); - ns.SendMoveBoatHS(beholder.Object, boat.Object, d, speed, list, xOffset, yOffset); + ns.SendMoveBoatHS(beholder.Object, boat, d, speed, boat.GetMovingEntities(true), xOffset, yOffset); var result = ns.SendPipe.Reader.TryRead(); AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); @@ -53,8 +65,18 @@ namespace UOContent.Tests [Fact] public void TestDisplayBoatHS() { - var item1 = new Item((Serial)0x1000) { ItemID = 0x13B9, Location = new Point3D(11, 21, 16), Map = Map.Felucca }; - var item2 = new Item((Serial)0x2000) { ItemID = 0x13B9, Location = new Point3D(100, 200, 16), Map = Map.Felucca }; + var item1 = new Item((Serial)0x1000) + { + ItemID = 0x13B9, + Location = new Point3D(11, 21, 16), + Map = Map.Felucca + }; + var item2 = new Item((Serial)0x2000) + { + ItemID = 0x13B9, + Location = new Point3D(100, 200, 16), + Map = Map.Felucca + }; var beholder = new Mock((Serial)0x100); beholder.Object.DefaultMobileInit(); @@ -63,22 +85,45 @@ namespace UOContent.Tests beholder.Setup(m => m.CanSee(It.Is(e => e == item1))).Returns(true); beholder.Setup(m => m.CanSee(It.Is(e => e == item2))).Returns(false); - var boat = new Mock((Serial)0x3000); - boat.Setup(b => b.GetMovingEntities()).Returns(() => new List{ item1, beholder.Object }); - boat.Setup(b => b.Location).Returns(new Point3D(10, 20, 15)); - boat.Object.Facing = Direction.Right; + var list = new List { item1, beholder.Object }; + var notContained = new List { item2 }; + var boat = new TestBoat(0x3000, list, notContained) + { + Location = new Point3D(10, 20, 15), + Facing = Direction.Right + }; - beholder.Setup(m => m.CanSee(It.Is(e => e == boat.Object))).Returns(true); + beholder.Setup(m => m.CanSee(It.Is(e => e == boat))).Returns(true); using var ns = PacketTestUtilities.CreateTestNetState(); ns.ProtocolChanges = ProtocolChanges.HighSeas; - var expected = new DisplayBoatHS(beholder.Object, boat.Object).Compile(); + var expected = new DisplayBoatHS(beholder.Object, boat).Compile(); - ns.SendDisplayBoatHS(beholder.Object, boat.Object); + ns.SendDisplayBoatHS(beholder.Object, boat); var result = ns.SendPipe.Reader.TryRead(); AssertThat.Equal(result.Buffer[0].AsSpan(0), expected); } + + public class TestBoat : BaseBoat + { + private readonly List components; + private readonly List notContained; + + public TestBoat(Serial serial, List list, List notContainedList) : base(serial) + { + components = list; + notContained = notContainedList; + Components = new MultiComponentList(new List()); + } + + public override MultiComponentList Components { get; } + + public override bool Contains(int x, int y) => !notContained.Any(e => e.X == x && e.Y == y); + + public override MovingEntitiesEnumerable GetMovingEntities(bool includeBoat = false) => + new(this, true, new Map.PooledEnumerable(components)); + } } } diff --git a/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs b/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs index 812f183c0..730d40650 100644 --- a/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs +++ b/Projects/UOContent.Tests/Tests/Multis/Boats/Packets.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using Server; using Server.Collections; using Server.Items; @@ -52,16 +53,9 @@ namespace UOContent.Tests { public DisplayBoatHS(Mobile beholder, BaseBoat boat) : base(0xF7) { - var ents = boat.GetMovingEntities(); + var ents = boat.GetMovingEntities(true); - ents.AddNotNull(boat.TillerMan); - ents.AddNotNull(boat.Hold); - ents.AddNotNull(boat.PPlank); - ents.AddNotNull(boat.SPlank); - - ents.Add(boat); - - EnsureCapacity(3 + 2 + ents.Count * 26); + EnsureCapacity(3 + 2 + 5 * 26); Stream.Write((short)0); // count placeholder diff --git a/Projects/UOContent/Commands/Profiling.cs b/Projects/UOContent/Commands/Profiling.cs index fe32c4f93..b38bd3fb9 100644 --- a/Projects/UOContent/Commands/Profiling.cs +++ b/Projects/UOContent/Commands/Profiling.cs @@ -394,8 +394,17 @@ namespace Server.Commands { public int Compare(KeyValuePair x, KeyValuePair y) { - var aCount = x.Value.Aggregate(0, (t, val) => t + val); - var bCount = y.Value.Aggregate(0, (t, val) => t + val); + var aCount = 0; + foreach (var val in x.Value) + { + aCount += val; + } + + var bCount = 0; + foreach (var val in y.Value) + { + bCount += val; + } var v = -aCount.CompareTo(bCount); diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs index fede73408..880682c33 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using Server.Gumps; using Server.Items; @@ -472,7 +471,15 @@ namespace Server.Engines.ConPVP { var eable = Map.GetItemsInRange(point, 0); - var empty = eable.All(item => item == this); + var empty = true; + foreach (var item in eable) + { + if (item != this) + { + empty = false; + break; + } + } eable.Free(); diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index ff24915c8..442f87f2e 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -2711,9 +2711,15 @@ namespace Server.Gumps } else { - results = Accounts.GetAccounts() - .Where(acct => acct.Username.InsensitiveContains(match)) - .ToList(); + results = new List(); + foreach (var acct in Accounts.GetAccounts()) + { + if (acct.Username.InsensitiveContains(match)) + { + results.Add(acct); + } + } + results.Sort(AccountComparer.Instance); } diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index 79a135b3a..6e64cac73 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -1,6 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using Server.Collections; using Server.Items; using Server.Multis.Boats; @@ -736,11 +738,12 @@ namespace Server.Multis return DryDockResult.Items; } - var ents = GetMovingEntities(); + using var ents = GetMovingEntities(); + var enumerator = ents.GetEnumerator(); - if (ents.Count >= 1) + if (enumerator.MoveNext()) { - return ents[0] is Mobile ? DryDockResult.Mobiles : DryDockResult.Items; + return enumerator.Current is Mobile ? DryDockResult.Mobiles : DryDockResult.Items; } return DryDockResult.Valid; @@ -1737,11 +1740,8 @@ namespace Server.Multis } else { - var toMove = GetMovingEntities(); - toMove.AddNotNull(TillerMan); - toMove.AddNotNull(Hold); - toMove.AddNotNull(PPlank); - toMove.AddNotNull(SPlank); + using var eable = GetMovingEntities(true); + var enumerator = eable.GetEnumerator(); // Packet must be sent before actual locations are changed foreach (var ns in Map.GetClientsInRange(Location, GetMaxUpdateRange())) @@ -1750,11 +1750,12 @@ namespace Server.Multis if (ns.HighSeas && m.CanSee(this) && m.InRange(Location, GetUpdateRange(m))) { - ns.SendMoveBoatHS(m, this, d, clientSpeed, toMove, xOffset, yOffset); + ns.SendMoveBoatHS(m, this, d, clientSpeed, eable, xOffset, yOffset); + eable.Reset(); } } - foreach (var e in toMove) + foreach (var e in eable) { if (e is Item item) { @@ -1775,7 +1776,9 @@ namespace Server.Multis NoMoveHS = true; Location = new Point3D(X + xOffset, Y + yOffset, Z); - foreach (var e in toMove) + eable.Reset(); + + foreach (var e in eable) { if (e is Item item) { @@ -1795,12 +1798,8 @@ namespace Server.Multis public void Teleport(int xOffset, int yOffset, int zOffset) { - var toMove = GetMovingEntities(); - - for (var i = 0; i < toMove.Count; ++i) + foreach (var e in GetMovingEntities()) { - var e = toMove[i]; - if (e is Item item) { item.Location = new Point3D(item.X + xOffset, item.Y + yOffset, item.Z + zOffset); @@ -1814,47 +1813,115 @@ namespace Server.Multis Location = new Point3D(X + xOffset, Y + yOffset, Z + zOffset); } - public virtual List GetMovingEntities() + public virtual MovingEntitiesEnumerable GetMovingEntities(bool includeBoat = false) { - var list = new List(); - var map = Map; if (map == null || map == Map.Internal) { - return list; + return new MovingEntitiesEnumerable(this, includeBoat, null); } var mcl = Components; var eable = map.GetObjectsInBounds(new Rectangle2D(X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height)); + return new MovingEntitiesEnumerable(this, includeBoat, eable); + } - foreach (var o in eable) + public ref struct MovingEntitiesEnumerable + { + private readonly IPooledEnumerable _entities; + private readonly IEnumerator _enumerator; + private readonly bool _includeBoat; + private BaseBoat _boat; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MovingEntitiesEnumerable(BaseBoat boat, bool includeBoat, IPooledEnumerable entities) { - if (o == this || o is TillerMan || o is Hold || o is Plank) - { - continue; - } - - if (o is Item item) - { - if (Contains(item) && item.Visible && item.Z >= Z) - { - list.Add(item); - } - } - else if (o is Mobile m) - { - if (Contains(m)) - { - list.Add(m); - } - } + _entities = entities; + _enumerator = entities?.GetEnumerator(); + _boat = boat; + _includeBoat = includeBoat; } - eable.Free(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MovingEntitiesEnumerator GetEnumerator() => new(_boat, _includeBoat, _enumerator); - return list; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + _entities?.Free(); + _enumerator?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() + { + _enumerator?.Reset(); + } + } + + public ref struct MovingEntitiesEnumerator + { + private readonly IEnumerator _enumerator; + private IEntity? _current; + private readonly bool _includeBoat; + private readonly BaseBoat _boat; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MovingEntitiesEnumerator(BaseBoat boat, bool includeBoat, IEnumerator enumerator = null) + { + _enumerator = enumerator; + _current = default; + _includeBoat = includeBoat; + _boat = boat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + if (_enumerator?.MoveNext() != true) + { + return false; + } + + var current = _enumerator.Current; + + if (!_includeBoat && (current is TillerMan || current is Hold || current is Plank)) + { + return false; + } + + if (current is Item item) + { + if (item == _boat) + { + return false; + } + + if (_boat.Contains(item) && item.Visible && item.Z >= _boat.Z) + { + _current = current; + return true; + } + } + else if (current is Mobile m) + { + if (_boat.Contains(m)) + { + _current = current; + return true; + } + } + + return false; + } + + public IEntity Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } } public bool SetFacing(Direction facing) @@ -1916,36 +1983,37 @@ namespace Server.Multis SPlank?.SetFacing(facing); - var toMove = GetMovingEntities(); - - toMove.Add(PPlank); - toMove.Add(SPlank); - int xOffset = 0, yOffset = 0; Movement.Movement.Offset(facing, ref xOffset, ref yOffset); - if (TillerMan != null) - { - TillerMan.Location = new Point3D( - X + xOffset * TillerManDistance + (facing == Direction.North ? 1 : 0), - Y + yOffset * TillerManDistance, - TillerMan.Z - ); - } - if (Hold != null) { - Hold.Location = new Point3D(X + xOffset * HoldDistance, Y + yOffset * HoldDistance, Hold.Z); + } var count = (m_Facing - old) & 0x7; count /= 2; - for (var i = 0; i < toMove.Count; ++i) + foreach (var e in GetMovingEntities(true)) { - var e = toMove[i]; + if (e == this) + { + continue; + } - if (e is Item item) + if (e is TillerMan tiller) + { + tiller.Location = new Point3D( + X + xOffset * TillerManDistance + (facing == Direction.North ? 1 : 0), + Y + yOffset * TillerManDistance, + tiller.Z + ); + } + else if (e is Hold hold) + { + hold.Location = new Point3D(X + xOffset * HoldDistance, Y + yOffset * HoldDistance, hold.Z); + } + else if (e is Item item) { item.Location = Rotate(item.Location, count); } diff --git a/Projects/UOContent/Multis/Boats/BoatPackets.cs b/Projects/UOContent/Multis/Boats/BoatPackets.cs index 2c0d3a1b4..76680e886 100644 --- a/Projects/UOContent/Multis/Boats/BoatPackets.cs +++ b/Projects/UOContent/Multis/Boats/BoatPackets.cs @@ -13,11 +13,9 @@ * along with this program. If not, see . * *************************************************************************/ +using System; using System.Buffers; -using System.Collections.Generic; using System.IO; -using System.Linq; -using Server.Collections; using Server.Network; namespace Server.Multis.Boats @@ -25,15 +23,15 @@ namespace Server.Multis.Boats public static class BoatPackets { public static void SendMoveBoatHS(this NetState ns, Mobile beholder, BaseBoat boat, - Direction d, int speed, List ents, int xOffset, int yOffset) + Direction d, int speed, BaseBoat.MovingEntitiesEnumerable ents, int xOffset, int yOffset) { if (ns?.HighSeas != true) { return; } - var maxLength = 18 + ents.Count * 10; - var writer = new SpanWriter(stackalloc byte[maxLength]); + var minLength = 68; // 18 + 5 * 10 + var writer = new SpanWriter(stackalloc byte[minLength], true); writer.Write((byte)0xF6); // Packet ID writer.Seek(2, SeekOrigin.Current); @@ -71,18 +69,35 @@ namespace Server.Multis.Boats public static void SendDisplayBoatHS(this NetState ns, Mobile beholder, BaseBoat boat) { - var ents = boat.GetMovingEntities(); + if (ns?.HighSeas != true) + { + return; + } - ents.AddNotNull(boat.TillerMan); - ents.AddNotNull(boat.Hold); - ents.AddNotNull(boat.PPlank); - ents.AddNotNull(boat.SPlank); + bool isSA = ns.StygianAbyss; + bool isHS = ns.HighSeas; - ents.Add(boat); + var minLength = PacketContainerBuilder.MinPacketLength + + OutgoingEntityPackets.MaxWorldEntityPacketLength + * 5; // Minimum of boat, hold, planks, and the player - var eable = ents.Where(beholder.CanSee); + using var builder = new PacketContainerBuilder(stackalloc byte[minLength]); - ns.SendBatchEntities(eable, ents.Count); + Span buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength); + + foreach (var entity in boat.GetMovingEntities(true)) + { + if (!beholder.CanSee(entity)) + { + continue; + } + + buffer.InitializePacket(); + var bytesWritten = OutgoingEntityPackets.CreateWorldEntity(buffer, entity, isSA, isHS); + builder.Advance(bytesWritten); + } + + ns.Send(builder.Finalize()); } } } diff --git a/Projects/UOContent/Multis/Boats/Plank.cs b/Projects/UOContent/Multis/Boats/Plank.cs index 01892c790..82e7b7f5b 100644 --- a/Projects/UOContent/Multis/Boats/Plank.cs +++ b/Projects/UOContent/Multis/Boats/Plank.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Server.Factions; using Server.Multis; using Server.Network; @@ -226,7 +225,28 @@ namespace Server.Items return false; } - public bool CanClose() => Map != null && !Deleted && GetObjectsInRange(0).All(o => o == this); + public bool CanClose() + { + if (Map == null || Deleted) + { + return false; + } + + var eable = GetObjectsInRange(0); + + foreach (var obj in eable) + { + if (obj == this) + { + eable.Free(); + return true; + } + } + + eable.Free(); + + return false; + } public void Close() { diff --git a/Projects/UOContent/Multis/HousePlacementTool.cs b/Projects/UOContent/Multis/HousePlacementTool.cs index 7b5e51ecc..56817bf22 100644 --- a/Projects/UOContent/Multis/HousePlacementTool.cs +++ b/Projects/UOContent/Multis/HousePlacementTool.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Server.Gumps; using Server.Mobiles; using Server.Multis; @@ -2251,7 +2250,15 @@ namespace Server.Items if (obj is List list) { - return list.FirstOrDefault(e => e.MultiID == house.ItemID); + foreach (var hpe in list) + { + if (hpe.MultiID == house.ItemID) + { + return hpe; + } + } + + return null; } if (obj is Dictionary table) diff --git a/Projects/UOContent/Network/EntityPackets.cs b/Projects/UOContent/Network/EntityPackets.cs index be32718bb..b1e99a624 100644 --- a/Projects/UOContent/Network/EntityPackets.cs +++ b/Projects/UOContent/Network/EntityPackets.cs @@ -41,9 +41,11 @@ namespace Server.Network using var builder = new PacketContainerBuilder(stackalloc byte[minLength]); + Span buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength); + foreach (var entity in entities) { - Span buffer = builder.GetSpan(OutgoingEntityPackets.MaxWorldEntityPacketLength).InitializePacket(); + buffer.InitializePacket(); var bytesWritten = OutgoingEntityPackets.CreateWorldEntity(buffer, entity, isSA, isHS); builder.Advance(bytesWritten); }