rcon.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;
using rcon.Internal;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("rcon")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("rcon")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("61cbb59f-4d68-43c3-a12e-b2edaced107d")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace rcon
{
	public abstract class AbstractCommand : ICommand
	{
		protected BaseUnityPlugin Plugin { get; private set; }

		void ICommand.setOwner(BaseUnityPlugin owner)
		{
			Plugin = owner;
		}

		public abstract string onCommand(string[] args);
	}
	internal interface ICommand
	{
		void setOwner(BaseUnityPlugin owner);

		string onCommand(string[] args);
	}
	[BepInPlugin("nl.avii.plugins.rcon", "rcon", "1.1")]
	public class rcon : BaseUnityPlugin
	{
		public delegate string UnknownCommand(string command, string[] args);

		public delegate string ParamsAction(params object[] args);

		private AsynchronousSocketListener socketListener;

		private ConfigEntry<bool> Enabled;

		private ConfigEntry<int> Port;

		private ConfigEntry<string> Password;

		private Dictionary<string, Type> commands = new Dictionary<string, Type>();

		private Dictionary<string, ParamsAction> customCommands = new Dictionary<string, ParamsAction>();

		private Dictionary<string, BaseUnityPlugin> owners = new Dictionary<string, BaseUnityPlugin>();

		public event UnknownCommand OnUnknownCommand;

		private rcon()
		{
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("rcon", "enabled", false, "Enable RCON Communication");
			Port = ((BaseUnityPlugin)this).Config.Bind<int>("rcon", "port", 2458, "Port to use for RCON Communication");
			Password = ((BaseUnityPlugin)this).Config.Bind<string>("rcon", "password", "ChangeMe", "Password to use for RCON Communication");
		}

		private void OnEnable()
		{
			if (Enabled.Value)
			{
				socketListener = new AsynchronousSocketListener();
				socketListener.OnMessage += SocketListener_OnMessage;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("RCON Listening on port: " + Port.Value));
				socketListener.StartListening(Port.Value);
			}
		}

		private void SocketListener_OnMessage(Socket socket, int requestId, PacketType type, string payload)
		{
			switch (type)
			{
			case PacketType.Login:
			{
				string payload4 = "Login Success";
				if (payload.Trim() != Password.Value.Trim())
				{
					payload4 = "Login Failed";
					requestId = -1;
				}
				byte[] buffer = PacketBuilder.CreatePacket(requestId, PacketType.Command, payload4);
				socket.Send(buffer);
				break;
			}
			case PacketType.Command:
			{
				if (payload[0] == '/')
				{
					payload = payload.Substring(1);
				}
				List<string> list = (from Match m in Regex.Matches(payload, "(?<=[ ][\\\"]|^[\\\"])[^\\\"]+(?=[\\\"][ ]|[\\\"]$)|(?<=[ ]|^)[^\\\" ]+(?=[ ]|$)")
					select m.Value).ToList();
				string text = list[0].ToLower();
				list.RemoveAt(0);
				if (!commands.ContainsKey(text) && !customCommands.ContainsKey(text))
				{
					string text2 = this.OnUnknownCommand?.Invoke(text, list.ToArray());
					PacketType type2 = PacketType.Command;
					if (text2.ToLower().Contains("unknown"))
					{
						type2 = PacketType.Error;
					}
					socket.Send(PacketBuilder.CreatePacket(requestId, type2, text2));
				}
				else if (commands.ContainsKey(text))
				{
					ICommand obj = (ICommand)Activator.CreateInstance(commands[text]);
					obj.setOwner(owners[text]);
					string payload2 = obj.onCommand(list.ToArray());
					socket.Send(PacketBuilder.CreatePacket(requestId, type, payload2));
				}
				else if (customCommands.ContainsKey(text))
				{
					ParamsAction paramsAction = customCommands[text];
					object[] args = list.ToArray();
					string payload3 = paramsAction(args);
					socket.Send(PacketBuilder.CreatePacket(requestId, type, payload3));
				}
				break;
			}
			default:
				((BaseUnityPlugin)this).Logger.LogError((object)$"Unknown packet type: {type}");
				break;
			}
		}

		private void Update()
		{
			if (Enabled.Value && socketListener != null)
			{
				socketListener.Update();
			}
		}

		private void OnDisable()
		{
			if (Enabled.Value)
			{
				socketListener.Close();
			}
		}

		public void RegisterCommand<T>(BaseUnityPlugin owner, string command) where T : AbstractCommand, new()
		{
			command = command.ToLower();
			if (owners.ContainsKey(command))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)(command + " already registered"));
				return;
			}
			owners[command] = owner;
			commands[command] = typeof(T);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Registering Command: " + command));
		}

		public void RegisterCommand(BaseUnityPlugin owner, string command, ParamsAction action)
		{
			command = command.ToLower();
			if (owners.ContainsKey(command))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)(command + " already registered"));
				return;
			}
			owners[command] = owner;
			customCommands[command] = action;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Registering Command: " + command));
		}

		public void UnRegisterCommand(BaseUnityPlugin owner, string command)
		{
			if (owners.ContainsKey(command) && !((Object)(object)owners[command] != (Object)(object)owner))
			{
				owners.Remove(command);
				if (commands.ContainsKey(command))
				{
					commands.Remove(command);
				}
				if (customCommands.ContainsKey(command))
				{
					customCommands.Remove(command);
				}
			}
		}
	}
}
namespace rcon.Internal
{
	internal static class PacketBuilder
	{
		internal static byte[] CreatePacket(int requestId, PacketType type, string payload)
		{
			int num = 12 + payload.Length + 2;
			int num2 = 8 + payload.Length + 2;
			byte[] array = new byte[num];
			array[0] = (byte)num2;
			array[4] = (byte)requestId;
			array[8] = (byte)type;
			for (int i = 0; i < payload.Length; i++)
			{
				array[12 + i] = (byte)payload[i];
			}
			return array;
		}
	}
	internal class AsynchronousSocketListener
	{
		internal delegate void MessageReceived(Socket socket, int requestId, PacketType type, string payload);

		private Socket listener;

		private List<StateObject> clients = new List<StateObject>();

		internal event MessageReceived OnMessage;

		internal void StartListening(int port)
		{
			IPAddress any = IPAddress.Any;
			IPEndPoint localEP = new IPEndPoint(any, port);
			listener = new Socket(any.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
			try
			{
				listener.Bind(localEP);
				listener.Listen(100);
			}
			catch (Exception ex)
			{
				Debug.LogError((object)ex.ToString());
			}
		}

		private bool isConnected(Socket c)
		{
			try
			{
				if (c != null && c != null && c.Connected)
				{
					if (c.Poll(0, SelectMode.SelectRead))
					{
						return c.Receive(new byte[1], SocketFlags.Peek) != 0;
					}
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		internal void Update()
		{
			for (int i = 0; i < clients.Count; i++)
			{
				StateObject stateObject = clients[i];
				if (!isConnected(stateObject.workSocket))
				{
					Debug.Log((object)"Rcon client disconnected");
					stateObject.workSocket.Close();
					clients.Remove(stateObject);
				}
			}
			listener.BeginAccept(AcceptCallback, listener);
		}

		internal void AcceptCallback(IAsyncResult ar)
		{
			Socket socket = ((Socket)ar.AsyncState).EndAccept(ar);
			StateObject stateObject = new StateObject();
			stateObject.workSocket = socket;
			clients.Add(stateObject);
			Debug.Log((object)"Rcon client connected");
			socket.BeginReceive(stateObject.buffer, 0, 4096, SocketFlags.None, ReadCallback, stateObject);
		}

		private void ReadCallback(IAsyncResult ar)
		{
			_ = string.Empty;
			StateObject stateObject = (StateObject)ar.AsyncState;
			Socket workSocket = stateObject.workSocket;
			workSocket.EndReceive(ar);
			int num = BitConverter.ToInt32(stateObject.buffer, 0);
			int requestId = BitConverter.ToInt32(stateObject.buffer, 4);
			int type = BitConverter.ToInt32(stateObject.buffer, 8);
			num -= 10;
			byte[] array = new byte[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = stateObject.buffer[12 + i];
			}
			this.OnMessage?.Invoke(workSocket, requestId, (PacketType)type, Encoding.ASCII.GetString(array));
			stateObject.buffer = new byte[4096];
			workSocket.BeginReceive(stateObject.buffer, 0, 4096, SocketFlags.None, ReadCallback, stateObject);
		}

		private void Send(Socket handler, string data)
		{
			byte[] bytes = Encoding.ASCII.GetBytes(data);
			handler.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendCallback, handler);
		}

		private void SendCallback(IAsyncResult ar)
		{
			try
			{
				Socket obj = (Socket)ar.AsyncState;
				int num = obj.EndSend(ar);
				Debug.Log((object)$"Sent {num} bytes to client.");
				obj.Shutdown(SocketShutdown.Both);
				obj.Close();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)ex.ToString());
			}
		}

		internal void Close()
		{
			if (!listener.Connected)
			{
				return;
			}
			foreach (StateObject client in clients)
			{
				if (isConnected(client.workSocket))
				{
					client.workSocket.Close();
				}
			}
			listener.Close();
		}
	}
	internal enum PacketType
	{
		Error = -1,
		MultiPacket = 0,
		Command = 2,
		LoginResponse = 2,
		Login = 3
	}
	internal class StateObject
	{
		internal const int BufferSize = 4096;

		internal byte[] buffer = new byte[4096];

		internal Socket workSocket;
	}
}