diff --git a/APITest/APITest.csproj b/APITest/APITest.csproj
index 4cf446b..602cc1f 100644
--- a/APITest/APITest.csproj
+++ b/APITest/APITest.csproj
@@ -106,6 +106,7 @@
+
diff --git a/APITest/Commands/PlaceholderTest.cs b/APITest/Commands/PlaceholderTest.cs
new file mode 100644
index 0000000..5a8fe62
--- /dev/null
+++ b/APITest/Commands/PlaceholderTest.cs
@@ -0,0 +1,48 @@
+using APITest.UI;
+using APITest.UI.Elements;
+using CommandSystem;
+using Exiled.API.Features;
+using MEC;
+using NorthwoodLib.Pools;
+using NotAnAPI.API.Commands;
+using NotAnAPI.Features.PAPI.API;
+using NotAnAPI.Features.UI;
+using NotAnAPI.Features.UI.API.Abstract;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace APITest.Commands
+{
+
+ [CommandHandler(typeof(RemoteAdminCommandHandler))]
+ public class PlaceholderTest : NotCommands
+ {
+ public override string[] GetAliases() => [];
+
+ public override string GetCommandName() => "placeholder";
+
+ public override string GetDescription() => "Test placeholder.";
+
+ public override string[] GetPerms() => null;
+
+ public override bool GetRequirePlayer() => true;
+
+ public override string[] GetUsage() => ["string"];
+
+ public override bool PlayerBasedFunction(Player player, string[] args, out string result)
+ {
+ if (!TryGetArgument(args, 1, out string arg1))
+ {
+ result = "No Args!";
+ return true;
+ }
+
+ result = $"{PlaceholderAPI.SetPlaceholders(player, $"Hello %player_{arg1}%")}";
+ return false;
+ }
+
+ }
+}
diff --git a/APITest/Commands/Test.cs b/APITest/Commands/Test.cs
new file mode 100644
index 0000000..6b1cf44
--- /dev/null
+++ b/APITest/Commands/Test.cs
@@ -0,0 +1,61 @@
+using APITest.UI;
+using APITest.UI.Elements;
+using CommandSystem;
+using Exiled.API.Features;
+using Exiled.API.Interfaces;
+using Hints;
+using InventorySystem.Items.Firearms;
+using MEC;
+using Mirror;
+using NorthwoodLib.Pools;
+using NotAnAPI.API.Commands;
+using NotAnAPI.Features.UI;
+using NotAnAPI.Features.UI.API.Abstract;
+using RelativePositioning;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using UnityEngine;
+using YamlDotNet.Core.Tokens;
+using static EncryptedChannelManager;
+using static Unity.IO.LowLevel.Unsafe.AsyncReadManagerMetrics;
+
+namespace APITest.Commands
+{
+
+ [CommandHandler(typeof(RemoteAdminCommandHandler))]
+ public class Test : NotCommands
+ {
+ public override string[] GetAliases() => [];
+
+ public override string GetCommandName() => "test";
+
+ public override string GetDescription() => "test.";
+
+ public override string[] GetPerms() => null;
+
+ public override bool GetRequirePlayer() => true;
+
+ public override string[] GetUsage() => ["string"];
+
+ public override bool PlayerBasedFunction(Player player, string[] args, out string result)
+ {
+
+
+ SceneMessage message = new()
+ {
+ sceneName = "Facility",
+ };
+
+ player.Connection.Send(message);
+
+ player.SendConsoleMessage("Hello World", "red");
+
+ result = $"PersonalElement Enabled";
+ return false;
+ }
+
+ }
+}
diff --git a/NotAnAPI/API/Extensions/HashsetExtension.cs b/NotAnAPI/API/Extensions/HashsetExtension.cs
new file mode 100644
index 0000000..bff077f
--- /dev/null
+++ b/NotAnAPI/API/Extensions/HashsetExtension.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NotAnAPI.API.Extensions
+{
+ public static class HashsetExtension
+ {
+
+ public static bool TryGetValue(this HashSet set, T equalValue, out T actualValue)
+ {
+ if (set.Contains(equalValue))
+ {
+ actualValue = equalValue;
+ return true;
+ }
+ actualValue = default;
+ return false;
+ }
+
+ }
+}
diff --git a/NotAnAPI/Features/PAPI/API/Abstract/PlaceholderExpansion.cs b/NotAnAPI/Features/PAPI/API/Abstract/PlaceholderExpansion.cs
new file mode 100644
index 0000000..59e94a5
--- /dev/null
+++ b/NotAnAPI/Features/PAPI/API/Abstract/PlaceholderExpansion.cs
@@ -0,0 +1,28 @@
+using Exiled.API.Features;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NotAnAPI.Features.PAPI.API.Abstract
+{
+ public abstract class PlaceholderExpansion
+ {
+
+ public abstract string Author { get; set; }
+ public abstract string Identifier { get; set; }
+
+ public abstract string onRequest(Player player, string param);
+
+ public void Register()
+ {
+ PAPIFeature.Instance.Placeholders.Add(Identifier, this);
+ }
+
+ public void Unregister()
+ {
+ PAPIFeature.Instance.Placeholders.Remove(Identifier);
+ }
+ }
+}
diff --git a/NotAnAPI/Features/PAPI/API/PlaceholderAPI.cs b/NotAnAPI/Features/PAPI/API/PlaceholderAPI.cs
new file mode 100644
index 0000000..6513a5e
--- /dev/null
+++ b/NotAnAPI/Features/PAPI/API/PlaceholderAPI.cs
@@ -0,0 +1,43 @@
+using Discord;
+using Exiled.API.Features;
+using NotAnAPI.API.Extensions;
+using NotAnAPI.Features.PAPI.API.Abstract;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using UnityEngine.Windows;
+
+namespace NotAnAPI.Features.PAPI.API
+{
+ public static class PlaceholderAPI
+ {
+
+ private const string pattern = @"%(?[a-zA-Z0-9]+)_(?[^%]+)%";
+
+ private static readonly Regex regex = new Regex(pattern);
+
+ public static string SetPlaceholders(Player player, string text)
+ {
+ return regex.Replace(text, match =>
+ {
+ string identifier = match.Groups["identifier"].Value;
+ string parameters = match.Groups["params"].Value;
+
+ if (PAPIFeature.Instance.Placeholders.TryGetValue(identifier, out PlaceholderExpansion replacement))
+ {
+ string final = replacement.onRequest(player, parameters);
+
+ return final == null ? "NaN" : final;
+ }
+ else
+ {
+ return match.Value;
+ }
+ });
+ }
+
+ }
+}
diff --git a/NotAnAPI/Features/PAPI/PAPIFeature.cs b/NotAnAPI/Features/PAPI/PAPIFeature.cs
new file mode 100644
index 0000000..926b1ce
--- /dev/null
+++ b/NotAnAPI/Features/PAPI/PAPIFeature.cs
@@ -0,0 +1,44 @@
+using NotAnAPI.Features.Base;
+using NotAnAPI.Features.Logger;
+using NotAnAPI.Features.PAPI.API.Abstract;
+using NotAnAPI.Features.PAPI.impl;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NotAnAPI.Features.PAPI
+{
+ public class PAPIFeature : APIFeatures
+ {
+
+ public static PAPIFeature Instance { get; set; }
+
+ public static string Name { get; set; } = "UI";
+ public override bool IsEnabled { get; set; } = true;
+
+ public Dictionary Placeholders = new();
+
+ public override void OnEnable()
+ {
+ NotALogger.Info("PAPI Loaded");
+ Instance = this;
+
+ new PlayerPlaceholder().Register();
+
+ base.OnEnable();
+ }
+
+ public override void OnDisable()
+ {
+ NotALogger.Info("PAPI Unloaded");
+
+ new PlayerPlaceholder().Unregister();
+
+ Instance = null;
+ IsEnabled = false;
+ base.OnDisable();
+ }
+ }
+}
diff --git a/NotAnAPI/Features/PAPI/impl/PlayerPlaceholder.cs b/NotAnAPI/Features/PAPI/impl/PlayerPlaceholder.cs
new file mode 100644
index 0000000..a36ea25
--- /dev/null
+++ b/NotAnAPI/Features/PAPI/impl/PlayerPlaceholder.cs
@@ -0,0 +1,44 @@
+using Exiled.API.Features;
+using NotAnAPI.Features.PAPI.API.Abstract;
+
+namespace NotAnAPI.Features.PAPI.impl
+{
+ public class PlayerPlaceholder : PlaceholderExpansion
+ {
+ public override string Author { get; set; } = "NotZer0Two";
+ public override string Identifier { get; set; } = "player";
+
+ public override string onRequest(Player player, string param)
+ {
+ switch(param.ToLower())
+ {
+ case "name":
+ return player.Nickname;
+ case "displayname":
+ return player.DisplayNickname;
+ case "ip":
+ return player.IPAddress;
+ case "dnt":
+ return player.DoNotTrack ? "Enabled" : "Disabled";
+ case "health":
+ return player.Health.ToString();
+ case "maxhealth":
+ return player.MaxHealth.ToString();
+ case "ping":
+ return player.Ping.ToString();
+ case "id":
+ return player.RawUserId;
+ case "fullid":
+ return player.UserId;
+ case "x":
+ return player.Position.x.ToString();
+ case "y":
+ return player.Position.y.ToString();
+ case "z":
+ return player.Position.z.ToString();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/NotAnAPI/NotAnAPI.csproj b/NotAnAPI/NotAnAPI.csproj
index 6416e94..fcc89dd 100644
--- a/NotAnAPI/NotAnAPI.csproj
+++ b/NotAnAPI/NotAnAPI.csproj
@@ -249,12 +249,17 @@
+
+
+
+
+