fix: Removes GetObjectsInRange and fixes boat planks closing (#1579)

## BREAKING CHANGE

- Deletes `map.GetObjectsInRange` and `map.GetObejctsInBounds`

### Notes

Developers are expected to enumerate mobiles and items separately now using `map.GetMobilesInRange` and `map.GetItemsInRange`. This helps keep the code streamlined so we don't have to maintain multiple copies of ref struct enumerators that do the same thing.


### Fixes

- [X] Fixes bug with planks closing
- [X] Fixes issue with iterating items/mobiles from a null map
This commit is contained in:
Kamron Batman 2023-11-03 13:45:18 -07:00 • committed by GitHub
parent cb1638591b
commit 977fdc2c5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 543 additions and 461 deletions

View file

@ -2445,15 +2445,6 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0;
public IPooledEnumerable<IEntity> GetObjectsInRange(int range)
{
var map = m_Map;
return map == null
? PooledEnumeration.NullEnumerable<IEntity>.Instance
: map.GetObjectsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.ItemAtEnumerable<Item> GetItemsAt() =>
m_Map == null ? Map.ItemAtEnumerable<Item>.Empty : m_Map.GetItemsAt(m_Parent == null ? m_Location : GetWorldLocation());

View file

@ -21,6 +21,9 @@ namespace Server;
public partial class Map
{
private static ValueLinkList<Item> _emptyItemLinkList = new();
public static ref readonly ValueLinkList<Item> EmptyItemLinkList => ref _emptyItemLinkList;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemAtEnumerable<Item> GetItemsAt(Point3D p) => GetItemsAt<Item>(p);
@ -110,7 +113,15 @@ public partial class Map
{
_started = false;
_location = loc;
_linkList = ref map.GetRealSector(loc.m_X, loc.m_Y).Items;
if (map == null)
{
_linkList = ref EmptyItemLinkList;
}
else
{
_linkList = ref map.GetRealSector(loc.m_X, loc.m_Y).Items;
}
_version = 0;
_current = null;
}

View file

@ -21,6 +21,9 @@ namespace Server;
public partial class Map
{
private static ValueLinkList<Mobile> _emptyMobileLinkList = new();
public static ref readonly ValueLinkList<Mobile> EmptyMobileLinkList => ref _emptyMobileLinkList;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<Mobile> GetMobilesAt(Point3D p) => GetMobilesAt<Mobile>(p);
@ -110,7 +113,14 @@ public partial class Map
{
_started = false;
_location = loc;
_linkList = ref map.GetRealSector(loc.m_X, loc.m_Y).Mobiles;
if (map == null)
{
_linkList = ref EmptyMobileLinkList;
}
else
{
_linkList = ref map.GetRealSector(loc.m_X, loc.m_Y).Mobiles;
}
_version = 0;
_current = null;
}

View file

@ -865,25 +865,15 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
public IPooledEnumerable<StaticTile[]> GetMultiTilesAt(int x, int y) =>
PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1));
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p) => GetObjectsInRange(p, Core.GlobalMaxUpdateRange);
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range) =>
GetObjectsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
public IPooledEnumerable<IEntity> GetObjectsInBounds(Rectangle2D bounds) =>
PooledEnumeration.GetEntities(this, bounds);
public bool CanFit(
Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true,
bool requireSurface = true
) =>
CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface);
) => CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface);
public bool CanFit(
Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true,
bool requireSurface = true
) =>
CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface);
) => CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface);
public bool CanFit(
int x, int y, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true,

View file

@ -17,7 +17,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Server.Collections;
using Server.Items;
namespace Server;
@ -32,32 +31,13 @@ public static class PooledEnumeration
static PooledEnumeration()
{
EntitySelector = SelectEntities;
MultiSelector = SelectMultis;
MultiTileSelector = SelectMultiTiles;
}
public static Selector<IEntity> EntitySelector { get; set; }
public static Selector<Mobile> MobileSelector { get; set; }
public static Selector<BaseMulti> MultiSelector { get; set; }
public static Selector<StaticTile[]> MultiTileSelector { get; set; }
public static IEnumerable<IEntity> SelectEntities(Map.Sector s, Rectangle2D bounds)
{
var entities = new List<IEntity>(s.Mobiles.Count + s.Items.Count);
foreach (var mob in s.Mobiles)
{
entities.Add(mob);
}
foreach (var item in s.Items)
{
entities.Add(item);
}
return entities;
}
public static IEnumerable<BaseMulti> SelectMultis(Map.Sector s, Rectangle2D bounds)
{
var entities = new List<BaseMulti>(s.Multis.Count);
@ -126,9 +106,6 @@ public static class PooledEnumeration
}
}
public static PooledEnumerable<IEntity> GetEntities(Map map, Rectangle2D bounds) =>
PooledEnumerable<IEntity>.Instantiate(map, bounds, EntitySelector ?? SelectEntities);
public static PooledEnumerable<BaseMulti> GetMultis(Map map, Rectangle2D bounds) =>
PooledEnumerable<BaseMulti>.Instantiate(map, bounds, MultiSelector ?? SelectMultis);

View file

@ -4342,25 +4342,25 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
{
using var moveQueue = PooledRefQueue<IEntity>.Create(2048);
using var moveClientQueue = PooledRefQueue<Mobile>.Create(2048);
var eable = m_Map.GetObjectsInRange(m_Location, Core.GlobalMaxUpdateRange);
foreach (var o in eable)
foreach (var mob in m_Map.GetMobilesInRange(m_Location, Core.GlobalMaxUpdateRange))
{
if (o == this)
if (mob == this)
{
continue;
}
if (o is Mobile mob)
if (mob.NetState != null)
{
if (mob.NetState != null)
{
moveClientQueue.Enqueue(mob);
}
moveQueue.Enqueue(mob);
moveClientQueue.Enqueue(mob);
}
else if (o is Item item && item.HandlesOnMovement)
moveQueue.Enqueue(mob);
}
foreach (var item in m_Map.GetItemsInRange(m_Location, Core.GlobalMaxUpdateRange))
{
if (item.HandlesOnMovement)
{
moveQueue.Enqueue(item);
}
@ -5561,44 +5561,27 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
if (m_Map != null)
{
var eable = m_Map.GetObjectsInRange(m_Location, range);
foreach (var o in eable)
foreach (var heard in m_Map.GetMobilesInRange(m_Location, range))
{
if (o is Mobile heard)
if (!heard.CanSee(this) || !NoSpeechLOS && heard.Player && !heard.InLOS(this))
{
if (!heard.CanSee(this) || !NoSpeechLOS && heard.Player && !heard.InLOS(this))
{
continue;
}
if (heard.m_NetState != null)
{
hears.Add(heard);
}
if (heard.HandlesOnSpeech(this))
{
onSpeech.Add(heard);
}
for (var i = 0; i < heard.Items.Count; ++i)
{
var item = heard.Items[i];
if (item.HandlesOnSpeech)
{
onSpeech.Add(item);
}
if (item is Container container)
{
AddSpeechItemsFrom(onSpeech, container);
}
}
continue;
}
else if (o is Item item)
if (heard.m_NetState != null)
{
hears.Add(heard);
}
if (heard.HandlesOnSpeech(this))
{
onSpeech.Add(heard);
}
for (var i = 0; i < heard.Items.Count; ++i)
{
var item = heard.Items[i];
if (item.HandlesOnSpeech)
{
onSpeech.Add(item);
@ -5611,6 +5594,19 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
}
}
foreach (var item in m_Map.GetItemsInRange(m_Location, range))
{
if (item.HandlesOnSpeech)
{
onSpeech.Add(item);
}
if (item is Container container)
{
AddSpeechItemsFrom(onSpeech, container);
}
}
object mutateContext = null;
var mutatedText = text;
SpeechEventArgs mutatedArgs = null;
@ -6856,23 +6852,19 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
return;
}
var eable = m_Map.GetObjectsInRange(m_Location, Core.GlobalMaxUpdateRange);
foreach (var o in eable)
foreach (var m in m_Map.GetMobilesInRange(m_Location, Core.GlobalMaxUpdateRange))
{
if (o is Mobile m)
if (m != this && Utility.InUpdateRange(m_Location, m.m_Location))
{
if (m != this && Utility.InUpdateRange(m_Location, m.m_Location))
{
m_NetState.SendRemoveEntity(m.Serial);
}
m_NetState.SendRemoveEntity(m.Serial);
}
else if (o is Item item)
}
foreach (var item in m_Map.GetItemsInRange(m_Location, Core.GlobalMaxUpdateRange))
{
if (InRange(item.Location, item.GetUpdateRange(this)))
{
if (InRange(item.Location, item.GetUpdateRange(this)))
{
m_NetState.SendRemoveEntity(item.Serial);
}
m_NetState.SendRemoveEntity(item.Serial);
}
}
}
@ -6923,36 +6915,32 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
return;
}
var eable = m_Map.GetObjectsInRange(m_Location, Core.GlobalMaxUpdateRange);
foreach (var o in eable)
foreach (var m in m_Map.GetMobilesInRange(m_Location, Core.GlobalMaxUpdateRange))
{
if (o is Item item)
if (CanSee(m) && Utility.InUpdateRange(m_Location, m.m_Location))
{
if (CanSee(item) && InRange(item.Location, item.GetUpdateRange(this)))
ns.SendMobileIncoming(this, m);
if (ns.StygianAbyss)
{
item.SendInfoTo(ns);
ns.SendMobileHealthbar(m, Healthbar.Poison);
ns.SendMobileHealthbar(m, Healthbar.Yellow);
}
if (m.IsDeadBondedPet)
{
ns.SendBondedStatus(m.Serial, true);
}
m.SendOPLPacketTo(ns);
}
else if (o is Mobile m)
}
foreach (var item in m_Map.GetItemsInRange(m_Location, Core.GlobalMaxUpdateRange))
{
if (CanSee(item) && InRange(item.Location, item.GetUpdateRange(this)))
{
if (CanSee(m) && Utility.InUpdateRange(m_Location, m.m_Location))
{
ns.SendMobileIncoming(this, m);
if (ns.StygianAbyss)
{
ns.SendMobileHealthbar(m, Healthbar.Poison);
ns.SendMobileHealthbar(m, Healthbar.Yellow);
}
if (m.IsDeadBondedPet)
{
ns.SendBondedStatus(m.Serial, true);
}
m.SendOPLPacketTo(ns);
}
item.SendInfoTo(ns);
}
}
}
@ -7310,67 +7298,70 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
if (ourState != null)
{
// We are attached to a client, so it's a bit more complex. We need to send new items and people to ourself, and ourself to other clients
foreach (var o in map.GetObjectsInRange(newLocation, Core.GlobalMaxUpdateRange))
foreach (var m in map.GetMobilesInRange(newLocation, Core.GlobalMaxUpdateRange))
{
if (o is Item item)
if (m == this)
{
var range = item.GetUpdateRange(this);
var loc = item.Location;
if (!Utility.InRange(oldLocation, loc, range) && Utility.InRange(newLocation, loc, range) &&
CanSee(item))
{
item.SendInfoTo(ourState);
}
continue;
}
else if (o != this && o is Mobile m)
if (!Utility.InUpdateRange(newLocation, m.m_Location))
{
if (!Utility.InUpdateRange(newLocation, m.m_Location))
continue;
}
var inOldRange = Utility.InUpdateRange(oldLocation, m.m_Location);
var ns = m.m_NetState;
if (ns != null &&
(isTeleport && (!ns.HighSeas || !NoMoveHS) || !inOldRange) && m.CanSee(this))
{
ns.SendMobileIncoming(m, this);
if (ns.StygianAbyss)
{
continue;
ns.SendMobileHealthbar(this, Healthbar.Poison);
ns.SendMobileHealthbar(this, Healthbar.Yellow);
}
var inOldRange = Utility.InUpdateRange(oldLocation, m.m_Location);
var ns = m.m_NetState;
if (ns != null &&
(isTeleport && (!ns.HighSeas || !NoMoveHS) || !inOldRange) && m.CanSee(this))
if (IsDeadBondedPet)
{
ns.SendMobileIncoming(m, this);
if (ns.StygianAbyss)
{
ns.SendMobileHealthbar(this, Healthbar.Poison);
ns.SendMobileHealthbar(this, Healthbar.Yellow);
}
if (IsDeadBondedPet)
{
ns.SendBondedStatus(Serial, true);
}
SendOPLPacketTo(ns);
ns.SendBondedStatus(Serial, true);
}
if (inOldRange || !CanSee(m))
{
continue;
}
SendOPLPacketTo(ns);
}
ourState.SendMobileIncoming(this, m);
if (inOldRange || !CanSee(m))
{
continue;
}
if (ourState.StygianAbyss)
{
ourState.SendMobileHealthbar(m, Healthbar.Poison);
ourState.SendMobileHealthbar(m, Healthbar.Yellow);
}
ourState.SendMobileIncoming(this, m);
if (m.IsDeadBondedPet)
{
ourState.SendBondedStatus(m.Serial, true);
}
if (ourState.StygianAbyss)
{
ourState.SendMobileHealthbar(m, Healthbar.Poison);
ourState.SendMobileHealthbar(m, Healthbar.Yellow);
}
m.SendOPLPacketTo(ourState);
if (m.IsDeadBondedPet)
{
ourState.SendBondedStatus(m.Serial, true);
}
m.SendOPLPacketTo(ourState);
}
foreach (var item in map.GetItemsInRange(newLocation, Core.GlobalMaxUpdateRange))
{
var range = item.GetUpdateRange(this);
var loc = item.Location;
if (!Utility.InRange(oldLocation, loc, range) && Utility.InRange(newLocation, loc, range) &&
CanSee(item))
{
item.SendInfoTo(ourState);
}
}
}
@ -8098,10 +8089,6 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
public Map.ItemBoundsEnumerable<T> GetItemsInRange<T>(int range) where T : Item =>
m_Map == null ? Map.ItemBoundsEnumerable<T>.Empty : m_Map.GetItemsInRange<T>(m_Location, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IPooledEnumerable<IEntity> GetObjectsInRange(int range) =>
m_Map?.GetObjectsInRange(m_Location, range) ?? PooledEnumeration.NullEnumerable<IEntity>.Instance;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileAtEnumerable<Mobile> GetMobilesInRange() => GetMobilesInRange<Mobile>();

View file

@ -1762,7 +1762,7 @@ public static class Utility
span == default || span.IsEmpty || span.IsWhiteSpace();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool InTypeList(this Item item, Type[] types) => item.GetType().InTypeList(types);
public static bool InTypeList<T>(this T obj, Type[] types) => obj.GetType().InTypeList(types);
public static bool InTypeList(this Type t, Type[] types)
{