Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 92 additions & 49 deletions EXILED/Exiled.API/Features/Draw.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,6 +26,9 @@ namespace Exiled.API.Features
/// </summary>
public static class Draw
{
// smallest array that fits the largest default segment (17 for sphere)
private static readonly Vector3[] ArrayNonAlloc = new Vector3[17];

/// <summary>
/// Draws a line between two specified points.
/// </summary>
Expand All @@ -33,7 +39,10 @@ public static class Draw
/// <param name="players">A collection of <see cref="Player"/>s to show the line to.</param>
public static void Line(Vector3 start, Vector3 end, Color color, float duration, IEnumerable<Player> players = null)
{
Send(players, duration, color, start, end);
ArrayNonAlloc[0] = start;
ArrayNonAlloc[1] = end;

Send(players, duration, color, ArrayNonAlloc, 2);
}

/// <summary>
Expand All @@ -45,7 +54,7 @@ public static void Line(Vector3 start, Vector3 end, Color color, float duration,
/// <param name="players">A collection of <see cref="Player"/>s to show the path to.</param>
public static void Path(Vector3[] points, Color color, float duration, IEnumerable<Player> players = null)
{
Send(players, duration, color, points);
Send(players, duration, color, points, points.Length);
}

/// <summary>
Expand All @@ -61,8 +70,7 @@ public static void Path(Vector3[] points, Color color, float duration, IEnumerab
/// <param name="segments">The number of line segments used to draw the circle. Higher values result in a smoother circle.</param>
public static void Circle(Vector3 origin, Quaternion rotation, Vector3 scale, Color color, float duration, IEnumerable<Player> 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);
}

/// <summary>
Expand All @@ -77,11 +85,16 @@ public static void Circle(Vector3 origin, Quaternion rotation, Vector3 scale, Co
/// <param name="segments">The number of segments for the circles. Higher values result in a smoother sphere.</param>
public static void Sphere(Vector3 origin, Quaternion rotation, Vector3 scale, Color color, float duration, IEnumerable<Player> players = null, int segments = 16)
{
Vector3[] horizontal = GetCirclePoints(origin, rotation, scale, segments, true);
Send(players, duration, color, horizontal);
List<Player> list = players is null ? null : ListPool<Player>.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<Player>.Pool.Return(list);
}

/// <summary>
Expand Down Expand Up @@ -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<Player> list = players is null ? null : ListPool<Player>.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<Player>.Pool.Return(list);
}

/// <summary>
Expand All @@ -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<Player> list = players is null ? null : ListPool<Player>.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<Player>.Pool.Return(list);
}

/// <summary>
Expand All @@ -259,39 +285,44 @@ 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<Player> list = players is null ? null : ListPool<Player>.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<Player>.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;

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++)
Expand All @@ -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++)
Expand All @@ -324,27 +355,39 @@ private static Vector3[] GetArcPoints(Vector3 origin, Quaternion rotation, Vecto
return array;
}

private static void Send(IEnumerable<Player> players, float duration, Color color, params Vector3[] points)
private static void Send(IEnumerable<Player> 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<byte> 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<byte> 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);
}
}
}
}
}
Expand Down
Loading