diff --git a/EXILED/Exiled.API/Features/Draw.cs b/EXILED/Exiled.API/Features/Draw.cs index b53ef4320..5eb7361b9 100644 --- a/EXILED/Exiled.API/Features/Draw.cs +++ b/EXILED/Exiled.API/Features/Draw.cs @@ -8,10 +8,13 @@ namespace Exiled.API.Features { using System; + using System.Buffers; using System.Collections.Generic; using DrawableLine; + using Exiled.API.Features.Pools; + using Mirror; using UnityEngine; @@ -23,6 +26,9 @@ namespace Exiled.API.Features /// public static class Draw { + // smallest array that fits the largest default segment (17 for sphere) + private static readonly Vector3[] ArrayNonAlloc = new Vector3[17]; + /// /// Draws a line between two specified points. /// @@ -33,7 +39,10 @@ public static class Draw /// A collection of s to show the line to. public static void Line(Vector3 start, Vector3 end, Color color, float duration, IEnumerable players = null) { - Send(players, duration, color, start, end); + ArrayNonAlloc[0] = start; + ArrayNonAlloc[1] = end; + + Send(players, duration, color, ArrayNonAlloc, 2); } /// @@ -45,7 +54,7 @@ public static void Line(Vector3 start, Vector3 end, Color color, float duration, /// A collection of s to show the path to. public static void Path(Vector3[] points, Color color, float duration, IEnumerable players = null) { - Send(players, duration, color, points); + Send(players, duration, color, points, points.Length); } /// @@ -61,8 +70,7 @@ public static void Path(Vector3[] points, Color color, float duration, IEnumerab /// The number of line segments used to draw the circle. Higher values result in a smoother circle. public static void Circle(Vector3 origin, Quaternion rotation, Vector3 scale, Color color, float duration, IEnumerable players = null, bool horizontal = true, int segments = 16) { - Vector3[] circlePoints = GetCirclePoints(origin, rotation, scale, segments, horizontal); - Send(players, duration, color, circlePoints); + Send(players, duration, color, GetCirclePoints(origin, rotation, scale, ref segments, horizontal), segments); } /// @@ -77,11 +85,16 @@ public static void Circle(Vector3 origin, Quaternion rotation, Vector3 scale, Co /// The number of segments for the circles. Higher values result in a smoother sphere. public static void Sphere(Vector3 origin, Quaternion rotation, Vector3 scale, Color color, float duration, IEnumerable players = null, int segments = 16) { - Vector3[] horizontal = GetCirclePoints(origin, rotation, scale, segments, true); - Send(players, duration, color, horizontal); + List list = players is null ? null : ListPool.Pool.Get(players); + + Vector3[] array = GetCirclePoints(origin, rotation, scale, ref segments, true); + Send(list, duration, color, array, segments); + + array = GetCirclePoints(origin, rotation, scale, ref segments, false); + Send(list, duration, color, array, segments); - Vector3[] vertical = GetCirclePoints(origin, rotation, scale, segments, false); - Send(players, duration, color, vertical); + if (list != null) + ListPool.Pool.Return(list); } /// @@ -199,24 +212,31 @@ public static void Capsule(Vector3 center, Quaternion rotation, float height, fl Vector3 bottomCenter = center - (up * halfCylinderHeight); Vector3 ringScale = new(radius * sX, 1f, radius * sZ); - Circle(topCenter, rotation, ringScale, color, duration, players, horizontal: true); - Circle(bottomCenter, rotation, ringScale, color, duration, players, horizontal: true); + + List list = players is null ? null : ListPool.Pool.Get(players); + + Circle(topCenter, rotation, ringScale, color, duration, list); + Circle(bottomCenter, rotation, ringScale, color, duration, list); float rX = radius * sX; - Line(topCenter + (right * rX), bottomCenter + (right * rX), color, duration, players); - Line(topCenter - (right * rX), bottomCenter - (right * rX), color, duration, players); + Line(topCenter + (right * rX), bottomCenter + (right * rX), color, duration, list); + Line(topCenter - (right * rX), bottomCenter - (right * rX), color, duration, list); float rZ = radius * sZ; - Line(topCenter + (forward * rZ), bottomCenter + (forward * rZ), color, duration, players); - Line(topCenter - (forward * rZ), bottomCenter - (forward * rZ), color, duration, players); + Line(topCenter + (forward * rZ), bottomCenter + (forward * rZ), color, duration, list); + Line(topCenter - (forward * rZ), bottomCenter - (forward * rZ), color, duration, list); Vector3 arcScaleSide = new(radius * sZ, radius * sY, 1f); Vector3 arcScaleFront = new(radius * sX, radius * sY, 1f); - Send(players, duration, color, GetArcPoints(topCenter, rotation, arcScaleSide, 180f)); - Send(players, duration, color, GetArcPoints(topCenter, rotation * Quaternion.Euler(0, 90, 0), arcScaleFront, 180f)); - Send(players, duration, color, GetArcPoints(bottomCenter, rotation * Quaternion.Euler(180, 0, 0), arcScaleSide, 180f)); - Send(players, duration, color, GetArcPoints(bottomCenter, rotation * Quaternion.Euler(180, 90, 0), arcScaleFront, 180f)); + const int segments = 8; + Send(list, duration, color, GetArcPoints(topCenter, rotation, arcScaleSide, 180f, segments), segments); + Send(list, duration, color, GetArcPoints(topCenter, rotation * Quaternion.Euler(0, 90, 0), arcScaleFront, 180f, segments), segments); + Send(list, duration, color, GetArcPoints(bottomCenter, rotation * Quaternion.Euler(180, 0, 0), arcScaleSide, 180f, segments), segments); + Send(list, duration, color, GetArcPoints(bottomCenter, rotation * Quaternion.Euler(180, 90, 0), arcScaleFront, 180f, segments), segments); + + if (list != null) + ListPool.Pool.Return(list); } /// @@ -232,14 +252,20 @@ public static void Mesh(Mesh mesh, Transform transform, Color color, float durat int[] triangles = mesh.triangles; Vector3[] vertices = mesh.vertices; + List list = players is null ? null : ListPool.Pool.Get(players); + for (int i = 0; i < triangles.Length; i += 3) { - Vector3 p1 = transform.TransformPoint(vertices[triangles[i]]); - Vector3 p2 = transform.TransformPoint(vertices[triangles[i + 1]]); - Vector3 p3 = transform.TransformPoint(vertices[triangles[i + 2]]); + ArrayNonAlloc[0] = transform.TransformPoint(vertices[triangles[i]]); + ArrayNonAlloc[1] = transform.TransformPoint(vertices[triangles[i + 1]]); + ArrayNonAlloc[2] = transform.TransformPoint(vertices[triangles[i + 2]]); + ArrayNonAlloc[3] = ArrayNonAlloc[0]; - Path([p1, p2, p3, p1], color, duration, players); + Send(list, duration, color, ArrayNonAlloc, 4); } + + if (list != null) + ListPool.Pool.Return(list); } /// @@ -259,31 +285,36 @@ public static void Box(Vector3 center, Vector3 size, Quaternion rotation, Color float length = extents.z; float height = extents.y; - Vector3[] bottomRect = new Vector3[5]; - Vector3[] topRect = new Vector3[5]; + ArrayNonAlloc[0] = center + (rotation * new Vector3(-width, -height, -length)); + ArrayNonAlloc[1] = center + (rotation * new Vector3(width, -height, -length)); + ArrayNonAlloc[2] = center + (rotation * new Vector3(width, -height, length)); + ArrayNonAlloc[3] = center + (rotation * new Vector3(-width, -height, length)); + ArrayNonAlloc[4] = ArrayNonAlloc[0]; - bottomRect[0] = center + (rotation * new Vector3(-width, -height, -length)); - bottomRect[1] = center + (rotation * new Vector3(width, -height, -length)); - bottomRect[2] = center + (rotation * new Vector3(width, -height, length)); - bottomRect[3] = center + (rotation * new Vector3(-width, -height, length)); - bottomRect[4] = bottomRect[0]; + ArrayNonAlloc[5] = center + (rotation * new Vector3(-width, height, -length)); + ArrayNonAlloc[6] = center + (rotation * new Vector3(width, height, -length)); + ArrayNonAlloc[7] = center + (rotation * new Vector3(width, height, length)); + ArrayNonAlloc[8] = center + (rotation * new Vector3(-width, height, length)); + ArrayNonAlloc[9] = ArrayNonAlloc[5]; - topRect[0] = center + (rotation * new Vector3(-width, height, -length)); - topRect[1] = center + (rotation * new Vector3(width, height, -length)); - topRect[2] = center + (rotation * new Vector3(width, height, length)); - topRect[3] = center + (rotation * new Vector3(-width, height, length)); - topRect[4] = topRect[0]; + // reduce enumeration + List list = players is null ? null : ListPool.Pool.Get(players); - Send(players, duration, color, bottomRect); - Send(players, duration, color, topRect); + Send(list, duration, color, ArrayNonAlloc, 5); + Send(list, duration, color, ArrayNonAlloc, 5, 5); for (int i = 0; i < 4; i++) { - Send(players, duration, color, bottomRect[i], topRect[i]); + ArrayNonAlloc[10] = ArrayNonAlloc[i]; + ArrayNonAlloc[11] = ArrayNonAlloc[i + 5]; + Send(list, duration, color, ArrayNonAlloc, 2, 10); } + + if (list != null) + ListPool.Pool.Return(list); } - private static Vector3[] GetCirclePoints(Vector3 origin, Quaternion rotation, Vector3 scale, int segments, bool horizontal) + private static Vector3[] GetCirclePoints(Vector3 origin, Quaternion rotation, Vector3 scale, ref int segments, bool horizontal) { if (segments <= 5) segments = 8; @@ -291,7 +322,7 @@ private static Vector3[] GetCirclePoints(Vector3 origin, Quaternion rotation, Ve if (segments % 2 != 0) segments++; - Vector3[] array = new Vector3[segments + 1]; + Vector3[] array = segments < 17 ? ArrayNonAlloc : new Vector3[segments + 1]; float num = MathF.PI * 2f / (float)segments; for (int i = 0; i < segments; i++) @@ -309,7 +340,7 @@ private static Vector3[] GetCirclePoints(Vector3 origin, Quaternion rotation, Ve private static Vector3[] GetArcPoints(Vector3 origin, Quaternion rotation, Vector3 scale, float angle, int segments = 8) { - Vector3[] array = new Vector3[segments + 1]; + Vector3[] array = segments < 17 ? ArrayNonAlloc : new Vector3[segments + 1]; float angleStep = (angle * Mathf.Deg2Rad) / segments; for (int i = 0; i <= segments; i++) @@ -324,27 +355,39 @@ private static Vector3[] GetArcPoints(Vector3 origin, Quaternion rotation, Vecto return array; } - private static void Send(IEnumerable players, float duration, Color color, params Vector3[] points) + private static void Send(IEnumerable players, float duration, Color color, Vector3[] points, int count, int offset = 0) { - if (points == null || points.Length < 2) + if (points == null || points.Length - offset < 2 || count - offset < 2) return; - DrawableLineMessage msg = new(duration, color, points); + ArraySegment data; + using (NetworkWriterPooled writer = NetworkWriterPool.Get()) + { + writer.WriteUShort((ushort)typeof(DrawableLineMessage).FullName.GetStableHashCode()); + writer.WriteFloatNullable(duration); + writer.WriteColorNullable(color); + writer.WriteInt(count); + for (int i = offset; i < count + offset; i++) + writer.Write(points[i]); + data = writer.ToArraySegment(); + } if (players != null) { - using NetworkWriterPooled writer = NetworkWriterPool.Get(); - NetworkMessages.Pack(msg, writer); - ArraySegment segment = writer.ToArraySegment(); - foreach (Player ply in players) { - ply?.Connection.Send(segment); + ply.Connection.Send(data); } } else { - NetworkServer.SendToReady(msg); + foreach (NetworkConnectionToClient connectionToClient in NetworkServer.connections.Values) + { + if (connectionToClient.isReady) + { + connectionToClient.Send(data); + } + } } } }