Decompiled source of Chalky v0.0.3

AndrewLin.Chalky.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Chalky.Core;
using Chalky.Core.Commands;
using Chalky.Patches;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using PurrNet;
using UnityEngine;
using UnityEngine.EventSystems;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AndrewLin.Chalky")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Chalky: Some painting support for the artsy folks!")]
[assembly: AssemblyFileVersion("0.0.3.0")]
[assembly: AssemblyInformationalVersion("0.0.3+ed4467b73bdf8481fabaa61ee1b693daa39b7931")]
[assembly: AssemblyProduct("AndrewLin.Chalky")]
[assembly: AssemblyTitle("AndrewLin.Chalky")]
[assembly: AssemblyVersion("0.0.3.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
public static class ChatUtils
{
	private static readonly Regex CommonTMPTagRegex = new Regex("<\\/?(color(=#[0-9a-fA-F]{3,8})?|#[0-9a-fA-F]{3,8}|b|i|u|s|sup|sub|size(=[\\d.]+%?)?|alpha=#[0-9a-fA-F]{2}|mark(=#[0-9a-fA-F]{3,8})?|uppercase|lowercase|smallcaps|nobr|noparse|sprite(=\\d+)?|link(=[^>]*)?)>", RegexOptions.IgnoreCase | RegexOptions.Compiled);

	public static void AddGlobalNotification(string text)
	{
		NetworkSingleton<TextChannelManager>.I.AddNotification("[Chalky] " + text);
	}

	public static string GetUserName()
	{
		return NetworkSingleton<TextChannelManager>.I.UserName;
	}

	public static void SendMessageAsync(string userName, string text, bool Islocal = false)
	{
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
		Transform mainPlayer = i.MainPlayer;
		byte[] bytes = Encoding.Unicode.GetBytes(text.Substring(0, Math.Min(text.Length, 250)));
		byte[] bytes2 = Encoding.Unicode.GetBytes(userName);
		string text2 = Traverse.Create((object)i).Field<string>("_playerId").Value ?? "0";
		i.SendMessageAsync(bytes, bytes2, Islocal, mainPlayer.position, text2, default(RPCInfo));
	}

	public static void CleanCommand()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		LockState lockState = (LockState)(NetworkSingleton<MusicManager>.I.IsActive ? 5 : 0);
		MonoSingleton<TaskManager>.I.SetLockState(lockState);
		EventSystem.current.SetSelectedGameObject((GameObject)null);
		if (MonoSingleton<UIManager>.I.MessageInput.text != "/help")
		{
			MonoSingleton<UIManager>.I.MessageInput.text = "";
		}
	}

	public static string CleanTMPTags(string input)
	{
		return CommonTMPTagRegex.Replace(input, string.Empty);
	}
}
namespace Chalky
{
	public static class BuildInfo
	{
		public const string Version = "0.0.3";
	}
	[BepInPlugin("com.andrewlin.ontogether.chalky", "Chalky", "0.0.3")]
	public class Plugin : BaseUnityPlugin
	{
		public static readonly string DefaultChatLogPath = Path.Combine(Paths.BepInExRootPath, "on_together_chat_log.txt");

		public static int chalkySize = 2;

		public static Color? chalkyColor = null;

		public const string ModGUID = "com.andrewlin.ontogether.chalky";

		public const string ModName = "Chalky";

		public const string ModVersion = "0.0.3";

		public static ConfigEntry<bool>? EnableFeature { get; private set; }

		public static ConfigEntry<bool>? ShowCommand { get; private set; }

		public static ConfigEntry<string>? BoardSaveDirectory { get; private set; }

		public static string SaveDir { get; private set; } = "";


		public static string? PlayerId { get; private set; }

		public static CommandManager? CommandProcessor { get; private set; }

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Chalky v0.0.3 is loaded!");
			InitConfig();
			Harmony val = new Harmony("com.andrewlin.ontogether.chalky");
			val.PatchAll(typeof(TextChannelManagerPatch));
			val.PatchAll(typeof(QuadPainterGPUPatch));
			CommandProcessor = new CommandManager();
		}

		private void InitConfig()
		{
			EnableFeature = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableFeature", true, "Enable or disable the mod feature.");
			ShowCommand = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowCommand", false, "Show the command in chat when used.");
			BoardSaveDirectory = ((BaseUnityPlugin)this).Config.Bind<string>("Boards", "BoardSaveDirectory", "~/on-together/chalkboard", "Directory where chalkboard saves are stored. '~' expands to your home folder.");
			SaveDir = ResolvePath(BoardSaveDirectory.Value);
			Directory.CreateDirectory(SaveDir);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Board save directory: " + SaveDir));
		}

		public static string ResolvePath(string path)
		{
			if (path.StartsWith("~"))
			{
				path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + path.Substring(1);
			}
			return Path.GetFullPath(path);
		}
	}
}
namespace Chalky.Patches
{
	[HarmonyPatch(typeof(QuadPainterGPU))]
	public class QuadPainterGPUPatch
	{
		private static ManualLogSource Logger = Logger.CreateLogSource("Chalky.QuadPainterGPUPatch");

		private static readonly FieldInfo _pixelsField = typeof(QuadPainterGPU).GetField("_pixelsToUpdate", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo _previousUVField = typeof(QuadPainterGPU).GetField("previousUV", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo _fillTheBlanksMethod = typeof(QuadPainterGPU).GetMethod("FillTheBlanks", BindingFlags.Instance | BindingFlags.NonPublic);

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		public static bool UpdatePrefix(QuadPainterGPU __instance)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.EnableFeature == null || !Plugin.EnableFeature.Value)
			{
				return true;
			}
			int chalkySize = Plugin.chalkySize;
			if (chalkySize <= 2)
			{
				return true;
			}
			if (!__instance.IsActive || (!MonoSingleton<DrawingManager>.I.IsHaveChalk() && !MonoSingleton<DrawingManager>.I.IsEraser))
			{
				return false;
			}
			if (MonoSingleton<DrawingManager>.I.IsEraser)
			{
				return true;
			}
			Vector2 val = (Vector2)_previousUVField.GetValue(__instance);
			if (Input.GetMouseButton(0))
			{
				RaycastHit val2 = default(RaycastHit);
				if (!Physics.Raycast(MonoSingleton<ClickManager>.I.GetMousePosRay().Item1, ref val2, 10f, 1 << LayerMask.NameToLayer("Drawing")) || (Object)(object)((Component)((RaycastHit)(ref val2)).collider).gameObject != (Object)(object)((Component)__instance).gameObject)
				{
					return false;
				}
				Vector2 textureCoord = ((RaycastHit)(ref val2)).textureCoord;
				Vector2Int gridSize = __instance.gridSize;
				float num = 2f / (float)((Vector2Int)(ref gridSize)).x;
				float num2 = 2f / (float)((Vector2Int)(ref gridSize)).y;
				int num3 = Mathf.CeilToInt((float)chalkySize / 2f);
				int chalkIndex = MonoSingleton<DrawingManager>.I.ChalkIndex;
				Vector2 val3 = default(Vector2);
				for (int i = 0; i < num3; i++)
				{
					for (int j = 0; j < num3; j++)
					{
						((Vector2)(ref val3))..ctor(textureCoord.x + (float)i * num, textureCoord.y + (float)j * num2);
						Vector2 val4 = (Vector2)((val.x > 0f) ? new Vector2(val.x + (float)i * num, val.y + (float)j * num2) : val);
						__instance.FillTheBlanksRPC(val3, val4, chalkIndex, false, false, default(RPCInfo));
						_fillTheBlanksMethod.Invoke(__instance, new object[6] { val3, val4, chalkIndex, false, false, true });
					}
				}
				_previousUVField.SetValue(__instance, textureCoord);
			}
			if (Input.GetMouseButtonUp(0))
			{
				Vector2 val5 = val;
				val5.x = -1f;
				_previousUVField.SetValue(__instance, val5);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(TextChannelManager))]
	public class TextChannelManagerPatch
	{
		private static ManualLogSource Logger = Logger.CreateLogSource("Chalky.TextChannelManagerPatch");

		[HarmonyPatch("OnEnterPressed")]
		[HarmonyPrefix]
		public static void OnEnterPressedPrefix()
		{
			string text = MonoSingleton<UIManager>.I.MessageInput.text;
			if (!string.IsNullOrEmpty(text) && text.StartsWith('/') && Plugin.EnableFeature != null && (Plugin.EnableFeature.Value || text.Contains("chalkytoggle")))
			{
				bool flag = Plugin.CommandProcessor?.ProcessInput(text) ?? false;
				ConfigEntry<bool>? showCommand = Plugin.ShowCommand;
				if (showCommand != null && !showCommand.Value && flag)
				{
					ChatUtils.CleanCommand();
				}
			}
		}
	}
}
namespace Chalky.Core
{
	public static class BoardIO
	{
		private static readonly ManualLogSource Logger = Logger.CreateLogSource("Chalky");

		public static List<string> ListSavedBoards()
		{
			if (!Directory.Exists(Plugin.SaveDir))
			{
				Logger.LogWarning((object)("[BoardIO] Save directory not found: " + Plugin.SaveDir));
				return new List<string>();
			}
			return (from f in Directory.GetFiles(Plugin.SaveDir, "*.chalkboard.json")
				select Path.GetFileNameWithoutExtension(f).Replace(".chalkboard", "")).ToList();
		}

		public static int ResolveBoard(string? indexArg)
		{
			DrawingManager i = MonoSingleton<DrawingManager>.I;
			if ((Object)(object)i == (Object)null)
			{
				Logger.LogError((object)"[BoardIO] DrawingManager not found.");
				return -1;
			}
			int result;
			int num = ((string.IsNullOrEmpty(indexArg) || !int.TryParse(indexArg, out result)) ? ((int)AccessTools.Field(typeof(DrawingManager), "_boardIndex").GetValue(i)) : result);
			if (num < 0 || num >= i.QuadPainterGPUS.Count)
			{
				Logger.LogError((object)($"[BoardIO] Invalid board index {num}. " + $"Valid range: 0–{i.QuadPainterGPUS.Count - 1}. " + "Interact with a board first, or pass an explicit index."));
				return -1;
			}
			return num;
		}

		public static void SaveBoard(int index, string name)
		{
			DrawingManager i = MonoSingleton<DrawingManager>.I;
			if ((Object)(object)i == (Object)null || index < 0 || index >= i.QuadPainterGPUS.Count)
			{
				Logger.LogError((object)$"[BoardIO] SaveBoard: invalid state or index {index}");
				return;
			}
			QuadPainterGPU obj = i.QuadPainterGPUS[index];
			int[][] array = obj.PaintColors.Select((IntList col) => col.Ints.ToArray()).ToArray();
			string text = Path.Combine(Plugin.SaveDir, name + ".chalkboard.json");
			File.WriteAllText(text, JsonConvert.SerializeObject((object)array));
			Logger.LogInfo((object)$"[BoardIO] Board {index} saved to {text}");
			SavePreview(obj, name);
		}

		public static void LoadBoard(int index, string name)
		{
			DrawingManager i = MonoSingleton<DrawingManager>.I;
			if ((Object)(object)i == (Object)null || index < 0 || index >= i.QuadPainterGPUS.Count)
			{
				Logger.LogError((object)$"[BoardIO] LoadBoard: invalid state or index {index}");
				return;
			}
			QuadPainterGPU val = i.QuadPainterGPUS[index];
			if (!((NetworkIdentity)val).isServer)
			{
				ChatUtils.AddGlobalNotification("You must be the host to load a board.");
				Logger.LogWarning((object)"[BoardIO] Load+sync requires you to be the host.");
				return;
			}
			string text = Path.Combine(Plugin.SaveDir, name + ".chalkboard.json");
			if (!File.Exists(text))
			{
				ChatUtils.AddGlobalNotification("Board file not found: " + name + ".chalkboard.json");
				Logger.LogError((object)("[BoardIO] File not found: " + text));
				return;
			}
			int[][] array = JsonConvert.DeserializeObject<int[][]>(File.ReadAllText(text));
			for (int j = 0; j < val.PaintColors.Length && j < array.Length; j++)
			{
				for (int k = 0; k < val.PaintColors[j].Ints.Count && k < array[j].Length; k++)
				{
					val.PaintColors[j].Ints[k] = array[j][k];
				}
			}
			Logger.LogInfo((object)("[BoardIO] PaintColors restored from " + text));
			RebuildRenderTexture(val, i);
			BroadcastToPlayers(val);
			Logger.LogInfo((object)$"[BoardIO] Board {index} loaded and synced to all players.");
		}

		private static void SavePreview(QuadPainterGPU board, string name)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			RenderTexture value = Traverse.Create((object)board).Field("_rt").GetValue<RenderTexture>();
			if ((Object)(object)value == (Object)null)
			{
				Logger.LogWarning((object)"[BoardIO] _rt is null - skipping PNG preview.");
				return;
			}
			Texture2D val = new Texture2D(((Texture)value).width, ((Texture)value).height, (TextureFormat)5, false);
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = value;
			val.ReadPixels(new Rect(0f, 0f, (float)((Texture)value).width, (float)((Texture)value).height), 0, 0);
			val.Apply();
			RenderTexture.active = active;
			string text = Path.Combine(Plugin.SaveDir, name + ".chalkboard.png");
			File.WriteAllBytes(text, ImageConversion.EncodeToPNG(val));
			Object.Destroy((Object)(object)val);
			Logger.LogInfo((object)("[BoardIO] Preview saved to " + text));
		}

		private static void RebuildRenderTexture(QuadPainterGPU board, DrawingManager dm)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			Traverse val = Traverse.Create((object)board);
			RenderTexture value = val.Field("_rt").GetValue<RenderTexture>();
			if ((Object)(object)value == (Object)null)
			{
				Logger.LogError((object)"[BoardIO] _rt is null - cannot rebuild render texture.");
				return;
			}
			RenderTexture active = RenderTexture.active;
			RenderTexture.active = value;
			GL.Clear(true, true, Color.clear);
			RenderTexture.active = active;
			Traverse val2 = val.Method("PaintPixel", new Type[3]
			{
				typeof(Vector2Int),
				typeof(Color),
				typeof(int)
			}, (object[])null);
			for (int i = 0; i < board.PaintColors.Length; i++)
			{
				for (int j = 0; j < board.PaintColors[i].Ints.Count; j++)
				{
					int num = board.PaintColors[i].Ints[j];
					if (num != 0)
					{
						Color color = dm.GetColor(num - 1);
						val2.GetValue(new object[3]
						{
							(object)new Vector2Int(i, j),
							color,
							num
						});
					}
				}
			}
			val.Method("RenderBatch", Array.Empty<object>()).GetValue();
			Logger.LogInfo((object)"[BoardIO] RenderTexture rebuilt from PaintColors.");
		}

		private static void BroadcastToPlayers(QuadPainterGPU board)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerID> playerIDs = NetworkSingleton<PlayerPanelController>.I.PlayerIDs;
			int num = 0;
			foreach (PlayerID item in playerIDs)
			{
				board.GetQuadImage(item, board.PaintColors, default(RPCInfo));
				num++;
			}
			Logger.LogInfo((object)$"[BoardIO] Broadcast to {num} player(s).");
		}
	}
	public class CommandArgs
	{
		public static readonly CommandArgs EMPTY = new CommandArgs("", new string[0]);

		public string Name { get; }

		public string[] Args { get; }

		public CommandArgs(string name, string[] args)
		{
			Name = name;
			Args = args;
		}

		public static bool TryParse(string input, out CommandArgs result)
		{
			if (string.IsNullOrWhiteSpace(input) || !input.StartsWith('/'))
			{
				result = EMPTY;
				return false;
			}
			string text = input.Trim();
			string[] array = text.Substring(1, text.Length - 1).Split(' ', StringSplitOptions.RemoveEmptyEntries);
			string name = array[0].ToLower();
			string[] args = array.Skip(1).ToArray();
			result = new CommandArgs(name, args);
			return true;
		}

		public override string ToString()
		{
			return "/" + Name + " " + string.Join(' ', Args);
		}
	}
	public class CommandManager
	{
		private readonly ManualLogSource log = Logger.CreateLogSource("Chalky");

		private readonly Dictionary<string, IChatCommand> commands;

		public CommandManager()
		{
			IEnumerable<Type> enumerable = from t in Assembly.GetExecutingAssembly().GetTypes()
				where typeof(IChatCommand).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract
				select t;
			commands = new Dictionary<string, IChatCommand>();
			foreach (Type item in enumerable)
			{
				if (Activator.CreateInstance(item) is IChatCommand chatCommand)
				{
					commands[chatCommand.Name] = chatCommand;
					commands[chatCommand.ShortName] = chatCommand;
					log.LogInfo((object)("Loaded /" + chatCommand.Name + " (/" + chatCommand.ShortName + ")"));
				}
			}
			InitializeHelpCommand(commands);
		}

		private static void InitializeHelpCommand(Dictionary<string, IChatCommand> commands)
		{
			if (commands.TryGetValue("chalkyhelp", out IChatCommand value))
			{
				(value as ChalkyHelpCommand)?.Initialize(commands.Values);
			}
			if (commands.TryGetValue("help", out IChatCommand value2))
			{
				(value2 as HelpCommand)?.Initialize(commands.Values);
			}
		}

		public bool ProcessInput(string input)
		{
			if (!CommandArgs.TryParse(input, out CommandArgs result))
			{
				return false;
			}
			if (commands.TryGetValue(result.Name, out IChatCommand value))
			{
				try
				{
					value.Execute(result.Args);
				}
				catch (Exception ex)
				{
					ChatUtils.AddGlobalNotification($"Error executing '{result}': {ex.Message}");
				}
				return true;
			}
			return false;
		}
	}
}
namespace Chalky.Core.Commands
{
	public class ChalkyGetBoardsCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkygetboards";

		public string Name => "chalkygetboards";

		public string ShortName => "cgb";

		public string Description => "List all saved chalkboards. Usage: /chalkygetboards.";

		public void Execute(string[] args)
		{
			List<string> list = BoardIO.ListSavedBoards();
			if (list.Count == 0)
			{
				ChatUtils.AddGlobalNotification("No saved chalkboards found.");
				return;
			}
			string text = string.Join(", ", list);
			ChatUtils.AddGlobalNotification("Saved chalkboards: " + text);
		}
	}
	public class ChalkyHelpCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkyhelp";

		private SortedSet<IChatCommand>? commands;

		public string Name => "chalkyhelp";

		public string ShortName => "ch";

		public string Description => "Lists all available commands.";

		public void Initialize(IEnumerable<IChatCommand> values)
		{
			commands = new SortedSet<IChatCommand>(values);
		}

		public void Execute(string[] args)
		{
			if (commands == null)
			{
				ChatUtils.AddGlobalNotification("No commands available!");
				return;
			}
			ChatUtils.AddGlobalNotification("Chalky Version (0.0.3).");
			ChatUtils.AddGlobalNotification("Available commands:");
			foreach (IChatCommand command in commands)
			{
				ChatUtils.AddGlobalNotification("/" + command.Name + " (/" + command.ShortName + ") :: " + command.Description);
			}
		}
	}
	public class ChalkyLoadBoardCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkyloadboard";

		public string Name => "chalkyloadboard";

		public string ShortName => "clb";

		public string Description => "Load a chalkboard from disk and sync to all players (HOST only feature). Usage: /chalkyloadboard [name] [index]. Omit [index] to use the currently active board.";

		public void Execute(string[] args)
		{
			string name = ((args.Length >= 1) ? args[0] : "default");
			int num = BoardIO.ResolveBoard((args.Length >= 2) ? args[1] : null);
			if (num < 0)
			{
				ChatUtils.AddGlobalNotification("Could not determine board index. Interact with a board first, or pass an explicit index. e.g. /chalkyloadboard rat_king 1");
			}
			else
			{
				BoardIO.LoadBoard(num, name);
			}
		}
	}
	public class ChalkySaveBoardCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkysaveboard";

		public string Name => "chalkysaveboard";

		public string ShortName => "csb";

		public string Description => "Save a chalkboard to disk. Usage: /chalkysaveboard [name] [index]. Omit [index] to use the currently active board.";

		public void Execute(string[] args)
		{
			string text = ((args.Length >= 1) ? args[0] : "default");
			int num = BoardIO.ResolveBoard((args.Length >= 2) ? args[1] : null);
			if (num < 0)
			{
				ChatUtils.AddGlobalNotification("Could not determine board index. Interact with a board first, or pass an explicit index. e.g. /chalkysaveboard rat_king 1");
				return;
			}
			BoardIO.SaveBoard(num, text);
			ChatUtils.AddGlobalNotification($"Board {num} saved as '{text}' in {Plugin.SaveDir}");
		}
	}
	public class ChalkySetSize : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkysetsize";

		public string Name => "chalkysetsize";

		public string ShortName => "css";

		public string Description => "Set the size of the chalk. Not persistent, will reset to default on game restart. Game Default: 2. Usage: /chalkysetsize [size]";

		public void Execute(string[] args)
		{
			if (Plugin.EnableFeature == null)
			{
				return;
			}
			int result;
			if (args.Length == 0)
			{
				Plugin.chalkySize = 2;
				ChatUtils.AddGlobalNotification("Reset chalk size.");
			}
			else if (int.TryParse(args[0], out result))
			{
				if (result < 1)
				{
					ChatUtils.AddGlobalNotification("Invalid size value. Please provide an integer value of 1 or greater.");
					return;
				}
				if (result == 1)
				{
					ChatUtils.AddGlobalNotification("Sorry! ): Size 1 is not possible atm. Please choose a size of 2 or greater. \nP.S. if you figure out how to do size 1 please let me know!");
					return;
				}
				Plugin.chalkySize = result;
				ChatUtils.AddGlobalNotification($"Chalk size is now set to {Plugin.chalkySize}.");
			}
			else
			{
				ChatUtils.AddGlobalNotification("Invalid size value. Please provide a valid integer.");
			}
		}
	}
	public class ShowChalkyCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkyshowcommand";

		public string Name => "chalkyshowcommand";

		public string ShortName => "cshc";

		public string Description
		{
			get
			{
				ConfigEntry<bool>? showCommand = Plugin.ShowCommand;
				return "Toggle Chalky show/hide Chalky command in chat.If enabled, user command such as '/chalkysetcolor' will be shown in chat. Current: " + ((showCommand != null && showCommand.Value) ? "shown" : "hidden");
			}
		}

		public void Execute(string[] args)
		{
			if (Plugin.ShowCommand != null)
			{
				Plugin.ShowCommand.Value = !Plugin.ShowCommand.Value;
				ChatUtils.AddGlobalNotification("Chalky user command is now " + (Plugin.ShowCommand.Value ? "shown" : "hidden") + ".");
			}
		}
	}
	public class ChalkyToggleCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "chalkytoggle";

		public string Name => "chalkytoggle";

		public string ShortName => "ct";

		public string Description => "Toggle Chalky feature on/off. ";

		public void Execute(string[] args)
		{
			if (Plugin.EnableFeature != null)
			{
				Plugin.EnableFeature.Value = !Plugin.EnableFeature.Value;
				ChatUtils.AddGlobalNotification("Chalky feature is now " + (Plugin.EnableFeature.Value ? "enabled" : "disabled") + ".");
			}
		}
	}
	public class DisabledChalkySetColor
	{
		public const string CMD = "chalkysetcolor";

		public string Name => "chalkysetcolor";

		public string ShortName => "csc";

		public string Description => "Set the color of the chalk. Not persistent, will reset to default on game restart. Usage: /chalkysetcolor [hex color]";

		public void Execute(string[] args)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.EnableFeature != null)
			{
				Color value = default(Color);
				if (args.Length == 0)
				{
					ChatUtils.AddGlobalNotification("Reset chalk color.");
					Plugin.chalkyColor = null;
				}
				else if (!ColorUtility.TryParseHtmlString(args[0], ref value))
				{
					ChatUtils.AddGlobalNotification("Invalid color value. Please provide a valid hex color code (e.g. #FF0000 for red).");
				}
				else
				{
					Plugin.chalkyColor = value;
					ChatUtils.AddGlobalNotification("Chalk color is now set to " + args[0] + ".");
				}
			}
		}
	}
	public class HelpCommand : IChatCommand, IComparable<IChatCommand>
	{
		public const string CMD = "help";

		private SortedSet<IChatCommand>? commands;

		public string Name => "help";

		public string ShortName => "h";

		public string Description => "Lists all available commands.";

		public void Initialize(IEnumerable<IChatCommand> values)
		{
			commands = new SortedSet<IChatCommand>(values);
		}

		public void Execute(string[] args)
		{
			if (args.Length == 0)
			{
				ChatUtils.AddGlobalNotification("/help chalky for Chalky commands");
			}
			else
			{
				if (!(args[0] == "chalky"))
				{
					return;
				}
				if (commands != null)
				{
					ChatUtils.AddGlobalNotification("Chalky Version (0.0.3).");
					ChatUtils.AddGlobalNotification("Available commands:");
					{
						foreach (IChatCommand command in commands)
						{
							ChatUtils.AddGlobalNotification("/" + command.Name + " (/" + command.ShortName + ") :: " + command.Description);
						}
						return;
					}
				}
				ChatUtils.AddGlobalNotification("No commands available!");
			}
		}
	}
	public interface IChatCommand : IComparable<IChatCommand>
	{
		string Name { get; }

		string ShortName => "";

		string Description { get; }

		void Execute(string[] args);

		new string ToString()
		{
			return Name;
		}

		int IComparable<IChatCommand>.CompareTo(IChatCommand? other)
		{
			if (other == null)
			{
				return 1;
			}
			return string.Compare(Name, other.Name, StringComparison.Ordinal);
		}
	}
}