diff --git a/Source/Assembly/ASMInfo.cs b/Source/Assembly/ASMInfo.cs new file mode 100644 index 0000000..a0e70df --- /dev/null +++ b/Source/Assembly/ASMInfo.cs @@ -0,0 +1,19 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bot.Assemble +{ + public class ASMInfo + { + public static string BotName = "ZBotCore"; + public static double BotVer = 5.0; + public static string GitPassword = "**REMOVED**"; + } +} diff --git a/Source/BotSession.cs b/Source/BotSession.cs new file mode 100644 index 0000000..ee86955 --- /dev/null +++ b/Source/BotSession.cs @@ -0,0 +1,49 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenMetaverse; +using System.Threading.Tasks; + +namespace Bot +{ + public sealed class BotSession + { + private static BotSession _inst = null; + private static readonly object lockHandle = new object(); + + static BotSession() + { + + } + + public static BotSession Instance + { + get + { + lock (lockHandle) + { + if (_inst == null) + { + _inst = new BotSession(); + } + return _inst; + } + } + } + + + public GridClient grid { get; set; } + public SysOut Logger { get; set; } + public MessageHandler.MessageHandleEvent MHE; + public MessageHandler MH; + + public MainConfiguration ConfigurationHandle; + } +} diff --git a/Source/CommandSystem/CommandGroup.cs b/Source/CommandSystem/CommandGroup.cs new file mode 100644 index 0000000..580f64f --- /dev/null +++ b/Source/CommandSystem/CommandGroup.cs @@ -0,0 +1,45 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + + +using System; +using System.Reflection; + + +namespace Bot.CommandSystem +{ + + [System.AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class CommandGroup : Attribute + { + public string Command; + public int minLevel; + public MethodInfo AssignedMethod; + public int arguments = 0; + public CommandHelp cmdUsage; + public MessageHandler.Destinations CommandSource; + + public CommandGroup(string Command, int minLevel, int argCount, string HelpText, MessageHandler.Destinations SourceType) + { + this.Command = Command; + this.minLevel = minLevel; + arguments = argCount; + CommandSource = SourceType; + cmdUsage = new CommandHelp(Command, minLevel, argCount, HelpText, SourceType); + + } + } + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class CommandGroupMaster : Attribute + { + public string CommandGroupName; + public CommandGroupMaster(string CmdGroupName) + { + CommandGroupName = CmdGroupName; + } + } +} diff --git a/Source/CommandSystem/CommandHelp.cs b/Source/CommandSystem/CommandHelp.cs new file mode 100644 index 0000000..6a0d619 --- /dev/null +++ b/Source/CommandSystem/CommandHelp.cs @@ -0,0 +1,69 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.IO; +using OpenMetaverse; + + +namespace Bot.CommandSystem +{ + public class CommandHelp + { + Help h; + + public bool hasGroupFlag() + { + if ((h.dests_allowed & MessageHandler.Destinations.DEST_GROUP) == MessageHandler.Destinations.DEST_GROUP) return true; + else return false; + } + + public static readonly string NoAdditionalArguments = "This command does not take any arguments"; + public struct Help + { + public string Name; + public int minLevel; + public int args; + public string Text; + public string sources; + public MessageHandler.Destinations dests_allowed; + } + public string GetUsage() + { + return "_\nCommand [" + h.Name + "]\n" + h.sources + "\nMinimum Level Required [" + h.minLevel.ToString() + "]\nTotal Arguments [" + h.args.ToString() + "]\nUsage: " + h.Text; + + } + public string RawUsage() + { + return "Usage: " + h.Text; + } + public CommandHelp(string CmdName, int minLevel, int argCount, string HelpText, MessageHandler.Destinations DESTS) + { + h = new Help(); + string Applicable = "Command can be used in ["; + if ((DESTS & MessageHandler.Destinations.DEST_LOCAL) == MessageHandler.Destinations.DEST_LOCAL) Applicable += "Local, "; + if ((DESTS & MessageHandler.Destinations.DEST_AGENT) == MessageHandler.Destinations.DEST_AGENT) Applicable += "IM, "; + if ((DESTS & MessageHandler.Destinations.DEST_GROUP) == MessageHandler.Destinations.DEST_GROUP) Applicable += "Group, "; + if ((DESTS & MessageHandler.Destinations.DEST_DISCORD) == MessageHandler.Destinations.DEST_DISCORD) Applicable += "Discord, "; + + if (Applicable.Substring(Applicable.Length - 1, 1) == " ") Applicable = Applicable.Substring(0, Applicable.Length - 2) + "]"; + + h.dests_allowed = DESTS; + h.args = argCount; + h.Name = CmdName; + h.minLevel = minLevel; + if (HelpText == "") HelpText = NoAdditionalArguments; + h.Text = HelpText; + h.sources = Applicable; + } + } +} diff --git a/Source/CommandSystem/CommandRegistry.cs b/Source/CommandSystem/CommandRegistry.cs new file mode 100644 index 0000000..67764e9 --- /dev/null +++ b/Source/CommandSystem/CommandRegistry.cs @@ -0,0 +1,260 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenMetaverse; +using Newtonsoft.Json; +using System.Reflection; +using System.Threading; + + +namespace Bot.CommandSystem +{ + public sealed class CommandRegistry + { + /* + * =============================== + * START SINGLETON PATTERN + * =============================== + */ + private static CommandRegistry _instance = null; + private static readonly object lockhandle = new object(); + + static CommandRegistry() + { + + } + + public static CommandRegistry Instance + { + get + { + lock (lockhandle) + { + if (_instance == null) + { + BotSession bs = BotSession.Instance; + _instance = new CommandRegistry(); + _instance.client = bs.grid; + _instance.config = bs.ConfigurationHandle; + _instance.Log = bs.Logger; + _instance.MHEx = bs.MHE; + _instance.LocateCommands(); + } + return _instance; + } + } + } + + /* + * ============================== + * SINGLETON PATTERN END + * Usage: CommandRegistry reg = CommandRegistry.Instance + * ============================== + */ + + + + // Define the registry + public Dictionary Cmds = new Dictionary(); + public GridClient client; + public SysOut Log; + public IConfig config; + public MessageHandler.MessageHandleEvent MHEx; + public void LocateCommands() + { + try + { + + int i = 0; + // Locate all commands-- + for (i = 0; i < AppDomain.CurrentDomain.GetAssemblies().Length; i++) + { + Assembly A = null; + try + { + A = AppDomain.CurrentDomain.GetAssemblies()[i]; + } + catch (Exception e) + { + // MHEx(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "") + } + if (A != null) + { + int ii = 0; + for (ii = 0; ii < A.GetTypes().Length; ii++) + { + Type T = null; + try + { + T = A.GetTypes()[ii]; + } + catch (Exception e) + { + + } + if (T != null) + { + + if (T.IsClass) + { + foreach (MethodInfo MI in T.GetMethods()) + { + CommandGroup[] Command = (CommandGroup[])MI.GetCustomAttributes(typeof(CommandGroup), false); + //var CommandO = MI.GetCustomAttributes(typeof(CommandGroup), false); + if (Command.Length > 0) + { + for (int ix = 0; ix < Command.Length; ix++) + { + CommandGroup CG = Command[ix]; + CG.AssignedMethod = MI; + + if (Cmds.ContainsKey(CG.Command) == false) + { + Console.WriteLine("DISCOVER: " + CG.Command); + Cmds.Add(CG.Command, CG); + } + } + } + + + } + } + } + } + } + } + Console.WriteLine("Discovered " + Cmds.Count.ToString() + " total commands"); + } + catch (ReflectionTypeLoadException e) + { + MHEx(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "FAILURE!!!\n \n[Assembly load failure]"); + foreach (Exception X in e.LoaderExceptions) + { + MHEx(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, X.Message + "\n \nSTACK: " + X.StackTrace); + } + } + + } + + public void PrintHelpAll(MessageHandler.Destinations dest, UUID uid) + { + + for (int i = 0; i < Cmds.Count; i++) + { + + + KeyValuePair kvp = Cmds.ElementAt(i); + + CommandHelp HE = kvp.Value.cmdUsage; + if (dest == MessageHandler.Destinations.DEST_GROUP) + { + if (!HE.hasGroupFlag()) + { + //return; + } + else + { + MHEx(dest, uid, HE.GetUsage()); + } + } + else + { + + MHEx(dest, uid, HE.GetUsage()); + } + + // MHEx(dest, uid, kvp.Value.cmdUsage.GetUsage()); + + } + } + + public void PrintHelp(MessageHandler.Destinations dest, string cmd, UUID uid) + { + try + { + + CommandHelp HE = Cmds[cmd].cmdUsage; + if (dest == MessageHandler.Destinations.DEST_GROUP) + { + if (!HE.hasGroupFlag()) + { + //return; // DO NOT SCHEDULE THIS HELP INFO FOR GROUP!!! + } + } + MHEx(dest, uid, Cmds[cmd].cmdUsage.GetUsage()); + } + catch (Exception e) + { + MHEx(dest, uid, "Error: Unrecognized command"); + } + } + + public void RunCommand(string cmdString, UUID user, int level, MessageHandler.MessageHandleEvent MHE, MessageHandler.Destinations source, UUID agentKey, string agentName) + { + MHEx = MHE; + int pos = 0; + string[] cmdStruct = cmdString.Split(' '); + int IgnoreCount = 0; + foreach (string S in cmdStruct) + { + if (IgnoreCount > 0) { IgnoreCount--; } + else + { + + // Search for Command + if (Cmds.ContainsKey(S)) + { + // this must be a command + // argument types will ALWAYS be structured like this: + // UUID, int, GridClient, [args up to argCount], optional:{SysOut} + + CommandGroup cgX = Cmds[S]; + if (level >= cgX.minLevel) + { + // Check that the destination is allowed. + // If not then skip this command entirely + MessageHandler.Destinations dests = cgX.CommandSource; + bool Allowed = false; + if ((dests & MessageHandler.Destinations.DEST_AGENT) == source) Allowed = true; + if ((dests & MessageHandler.Destinations.DEST_GROUP) == source) Allowed = true; + if ((dests & MessageHandler.Destinations.DEST_LOCAL) == source) Allowed = true; + + if (!Allowed) + { + IgnoreCount = cgX.arguments; + + } + else + { + + var ovj = Activator.CreateInstance(cgX.AssignedMethod.DeclaringType); + string[] additionalArgs = new string[cgX.arguments]; + IgnoreCount = cgX.arguments; + for (int i = 1; i <= cgX.arguments; i++) + { + additionalArgs[i - 1] = cmdStruct[pos + i]; + } + pos++; + //(UUID client, int level, GridClient grid, string[] additionalArgs, + //SysOut log, MessageHandler.MessageHandleEvent MHE, MessageHandler.Destinations source, + //CommandRegistry registry, UUID agentKey, string agentName) + Thread CommandThread = new Thread(() => cgX.AssignedMethod.Invoke(ovj, new object[] { user, level, client, additionalArgs, Log, MHE, source, this, agentKey, agentName })); + CommandThread.Start(); + } + } + } + } + } + } + + + + } +} diff --git a/Source/ConfigSystem/BaseConfig.cs b/Source/ConfigSystem/BaseConfig.cs new file mode 100644 index 0000000..957a205 --- /dev/null +++ b/Source/ConfigSystem/BaseConfig.cs @@ -0,0 +1,20 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Bot +{ + class BaseConfig : IConfig + { + public float ConfigVersion { get; set; } + public string ConfigFor { get; set; } + public List Data { get; set; } + } +} diff --git a/Source/ConfigSystem/IConfig.cs b/Source/ConfigSystem/IConfig.cs new file mode 100644 index 0000000..5c8a1da --- /dev/null +++ b/Source/ConfigSystem/IConfig.cs @@ -0,0 +1,23 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Bot +{ + public interface IConfig + { + float ConfigVersion { get; set; } + + string ConfigFor { get; set; } + + List Data { get; set; } + } +} diff --git a/Source/ConfigSystem/MainConfiguration.cs b/Source/ConfigSystem/MainConfiguration.cs new file mode 100644 index 0000000..1773ea1 --- /dev/null +++ b/Source/ConfigSystem/MainConfiguration.cs @@ -0,0 +1,57 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace Bot +{ + [Serializable()] + public class MainConfiguration : IConfig + { + public float ConfigVersion + { + get; set; + } + public string ConfigFor { get; set; } + public List Data + { + get; set; + } + + public string MainProgramDLL; + public string first { get; set; } + public string last { get; set; } + public string password { get; set; } + + //public License LicenseKey { get; set; } + public string ActivationCode { get; set; } + + public MainConfiguration() + { + + } + + public static MainConfiguration Load() + { + MainConfiguration X = new MainConfiguration(); + SerialManager sm = new SerialManager(); + try + { + X = sm.Read("Main"); + return X; + } + catch (FileNotFoundException e) + { + Console.WriteLine("Main.json was not found"); + return new MainConfiguration(); + } + } + } +} diff --git a/Source/IProgram.cs b/Source/IProgram.cs new file mode 100644 index 0000000..9e84e90 --- /dev/null +++ b/Source/IProgram.cs @@ -0,0 +1,30 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenMetaverse; + +namespace Bot +{ + public interface IProgram + { + void run(GridClient client, MessageHandler MH, CommandSystem.CommandRegistry registry); // Define the run command since a thread needs a entry point + + string getTick(); // Run every second to check for queued data. If queue exists, then it will be returned as a JSON string. + // getTick can reply with data for the serializer for instance. + + void passArguments(string data); // json!! + + string ProgramName { get; } + float ProgramVersion { get; } + + void LoadConfiguration(); + void onIMEvent(object sender, InstantMessageEventArgs e); + } +} diff --git a/Source/MessageHandler.cs b/Source/MessageHandler.cs new file mode 100644 index 0000000..b81c587 --- /dev/null +++ b/Source/MessageHandler.cs @@ -0,0 +1,171 @@ + +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenMetaverse; +using Newtonsoft.Json; +using System.IO; +using System.Threading; + + +namespace Bot +{ + public class MessageHandler + { + private List MSGQueue = new List(); + private List ActionQueue = new List(); + private List DiscordQueue = new List(); + public ManualResetEvent GroupJoinWaiter = new ManualResetEvent(false); + private SysOut Log = SysOut.Instance; + + + [Flags] + public enum Destinations + { + DEST_AGENT = 1, + DEST_GROUP = 2, + DEST_LOCAL = 4, + DEST_CONSOLE_DEBUG = 8, + DEST_CONSOLE_INFO = 16, + DEST_ACTION = 32, + DEST_DISCORD = 64 + }; + + public struct MessageQueuePacket + { + public Destinations Dest; + public UUID DestID; + public string Msg; + public int channel; + } + + public struct ActionPacket + { + public Destinations Dest; + public string ActionStr; + } + + public struct DiscordAction + { + public string Action; + } + + public delegate void MessageHandleEvent(MessageHandler.Destinations DType, UUID AgentOrSession, string MSG, int channel = 0); + public volatile MessageHandleEvent callbacks; + public void MessageHandle(Destinations DType, UUID AgentOrSession, string MSG, int channel = 0) + { + if (DType == Destinations.DEST_ACTION) + { + if (MSG == "RESET_QUEUE") + { + ClearQueues(); + return; + } + ActionPacket PKT = new ActionPacket(); + PKT.Dest = DType; + PKT.ActionStr = MSG; + ActionQueue.Add(PKT); + return; + } + else if (DType == Destinations.DEST_DISCORD) + { + DiscordAction DA = new DiscordAction(); + DA.Action = MSG; + DiscordQueue.Add(DA); + return; // Do nothing + } + MessageQueuePacket pkt = new MessageQueuePacket(); + pkt.channel = channel; + pkt.Dest = DType; + pkt.DestID = AgentOrSession; + pkt.Msg = MSG; + + if (MSGQueue != null) + MSGQueue.Add(pkt); + } + + + public void ClearQueues() + { + MSGQueue = new List(); + DiscordQueue = new List(); + } + + public void run(GridClient client) + { + // Execute one queue item + if (MSGQueue.Count == 0) return; + MessageQueuePacket pkt = MSGQueue.First(); + MSGQueue.RemoveAt(MSGQueue.IndexOf(pkt)); + if (pkt.Dest == Destinations.DEST_AGENT) + { + client.Self.InstantMessage(pkt.DestID, "[" + MSGQueue.Count.ToString() + "] " + pkt.Msg); + } + else if (pkt.Dest == Destinations.DEST_CONSOLE_DEBUG) + { + Log.debug("[" + MSGQueue.Count.ToString() + "] " + pkt.Msg); + } + else if (pkt.Dest == Destinations.DEST_CONSOLE_INFO) + { + Log.info("[" + MSGQueue.Count.ToString() + "] " + pkt.Msg); + } + else if (pkt.Dest == Destinations.DEST_GROUP) + { + if (client.Self.GroupChatSessions.ContainsKey(pkt.DestID)) + client.Self.InstantMessageGroup(pkt.DestID, "[" + MSGQueue.Count.ToString() + "] " + pkt.Msg); + else + { + GroupJoinWaiter.Reset(); + client.Groups.ActivateGroup(pkt.DestID); + client.Self.RequestJoinGroupChat(pkt.DestID); + //callbacks(Destinations.DEST_LOCAL, UUID.Zero, "Attempting to join group chat for secondlife:///app/group/" + pkt.DestID.ToString() + "/about"); + + if (GroupJoinWaiter.WaitOne(TimeSpan.FromSeconds(20), false)) + { + + client.Self.InstantMessageGroup(pkt.DestID, "[" + MSGQueue.Count.ToString() + "] " + pkt.Msg); + } + else + { + MSGQueue.Add(pkt); // Because we failed to join the group chat we'll tack this onto the end of the queue and try again + + } + } + } + else if (pkt.Dest == Destinations.DEST_LOCAL) + { + client.Self.Chat("[" + MSGQueue.Count.ToString() + "] " + pkt.Msg, pkt.channel, ChatType.Normal); + } + } + + public string CheckActions() + { + string RETURNStr = ""; + if (ActionQueue.Count == 0) return "NONE"; + else + { + RETURNStr = ActionQueue.First().ActionStr; + ActionQueue.Remove(ActionQueue.First()); + return RETURNStr; + } + } + + public string CheckDiscordActions() + { + if (DiscordQueue.Count == 0) return "NONE"; + else + { + string RET = DiscordQueue.First().Action; + DiscordQueue.Remove(DiscordQueue.First()); + return RET; + } + } + } +} diff --git a/Source/PluginActivator.cs b/Source/PluginActivator.cs new file mode 100644 index 0000000..bbb6377 --- /dev/null +++ b/Source/PluginActivator.cs @@ -0,0 +1,44 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Bot +{ + public class PluginActivator + { + public Assembly LoadedASM = null; + public void LoadLibrary(string DLL) + { + + LoadedASM = Assembly.LoadFrom(DLL); + } + + public List Activate(Assembly asm) + { + List Plugins = new List(); + foreach (Type A in asm.GetTypes()) + { + Type check = A.GetInterface("IProgram"); + if (check == null) + { + //return null; + } + else + { + IProgram plugin = Activator.CreateInstance(A) as IProgram; + Plugins.Add(plugin); + } + } + return Plugins; + } + } +} diff --git a/Source/Program.cs b/Source/Program.cs new file mode 100644 index 0000000..be904b0 --- /dev/null +++ b/Source/Program.cs @@ -0,0 +1,743 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using Bot.CommandSystem; +using Newtonsoft.Json; +using OpenMetaverse; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using OpenMetaverse.Packets; +using Bot.Assemble; +using OpenMetaverse.Interfaces; +using System.Security.Cryptography; + +namespace Bot +{ + public class Program + { + public static SysOut Log = SysOut.Instance; + public static double BotVer = ASMInfo.BotVer; + public static string BotStr = ASMInfo.BotName; // internal identifier for linden + public static MainConfiguration conf; + public static string Flavor = "Bot"; // inworld identification - must be customized + public static SerialManager SM = new SerialManager(); + public static string DefaultProgram = "BlankBot.dll"; // default bot - blank will only contain the commands to switch programs. It is a complete blank! + public static GridClient client = new GridClient(); + public static bool g_iIsRunning = true; + public static MessageHandler MH; + public static CommandRegistry registry; + public static List g_ZPrograms = new List(); + + static readonly object _CacheLock = new object(); + //public static License LicenseKey; // Not to be used yet + + public static void msg(MessageHandler.Destinations D, UUID x, string m) + { + MH.callbacks(D, x, m); + } + public static unsafe void Main(string[] args) + { + Console.WriteLine("Setting up Main Configuration"); + conf = MainConfiguration.Load(); + //MasterObjectCaches = ObjectCaches.Instance; + Log.debugf(true, "main", args); + + if (args.Length == 2) + { + // Check if this is activation command + if (args[0] == "-a") + { + conf.ActivationCode = args[1]; + SM.Write("Main", conf); + return; + } + else if (args[0] == "-m") + { + conf.MainProgramDLL = args[1]; + SM.Write("Main", conf); + return; + } + } + else if (args.Length == 4) + { + if (args[0] == "-l") + { + conf.first = args[1]; + conf.last = args[2]; + conf.password = args[3]; + SM.Write("Main", conf); + return; + } + } + // Initiate bot login + // Main thread must be caught in the bot loop so it does not terminate early. + // Other programs may hook into the bot to control it + + /* + if (conf.ActivationCode != "") + { + + License L = new License(); + L.NewKey(); + L.InitUniqueMachine(); + L.Key = conf.ActivationCode; + License def = new License(); + def.NewKey(); + def.InitUniqueMachine(); + string Reply; + HttpWebRequest _request = (HttpWebRequest)WebRequest.Create("http://bak.cloud.xsinode.net/act_handle.php?r=verify&act=" + conf.ActivationCode + "&LIC=" + L.Key + "&mac="+def.Key); + using (HttpWebResponse response = (HttpWebResponse)_request.GetResponse()) + using (Stream str = response.GetResponseStream()) + using (StreamReader sr = new StreamReader(str)) + { + Reply = sr.ReadToEnd(); + } + + string[] ReplyData = Reply.Split('|'); + if(ReplyData[0] == "deny") + { + WebClient wc = new WebClient(); + if (File.Exists("Activator.exe")) File.Delete("Activator.exe"); + wc.DownloadFile("http://bak.cloud.xsinode.net/znibot/Activator.exe", "Activator.exe"); + int E_CODE = -1; + if (ReplyData[1] == "TOO_MANY") E_CODE = 90; + else if (ReplyData[1] == "EXPIRED") E_CODE = 32; + else if (ReplyData[1] == "INVALID") E_CODE = 100; + else E_CODE = 55; + + string batchContents = "@echo off" + + "\ntimeout 5\n" + + "cd " + Directory.GetCurrentDirectory()+"\n" + + "Activator.exe -d -m " + E_CODE.ToString(); + File.WriteAllText("denyAct.bat", batchContents); + Process p = Process.Start("denyAct.bat"); + + return; + + } else if(ReplyData[0] == "allow") + { + // Handle expiry on server! + // Activate now + } + else + { + + WebClient wc = new WebClient(); + if (File.Exists("Activator.exe")) File.Delete("Activator.exe"); + wc.DownloadFile("http://bak.cloud.xsinode.net/znibot/Activator.exe", "Activator.exe"); + int E_CODE = 55; // Unknown reply. Activator will not permit logging in until the server can be reached safely. + + string batchContents = "@echo off" + + "\ntimeout 5\n" + + "cd " + Directory.GetCurrentDirectory() + "\n" + + "Activator.exe -d -m " + E_CODE.ToString(); + File.WriteAllText("denyAct.bat", batchContents); + Process p = Process.Start("denyAct.bat"); + + return; + } + } + else + { + Console.WriteLine("ERROR: You must have an activation code set prior to running Bot.exe!!\n \n[Please run Activator with the Confirmation Number]"); + Console.ReadKey(); + return; + } + */ + MH = new MessageHandler(); + MH.callbacks += MH.MessageHandle; + + + string fna = null; + string lna = null; + string pwd = null; + if (conf.first == null) + { + + if (args.Length == 0) + { + + Log.info("Please enter your avatar's first name: "); + fna = Console.ReadLine(); + + Log.info("Please enter the last name: "); + lna = Console.ReadLine(); + Log.info("Now enter your password: "); + pwd = Console.ReadLine(); + + conf.MainProgramDLL = DefaultProgram; + conf.ConfigFor = "ZBotCore"; + conf.ConfigVersion = 1.0f; + + } + else + { + Log.info("Loading..."); + Log.info("FirstName: " + args[0]); + fna = args[0]; + lna = args[1]; + pwd = args[2]; + + // Continue boot + } + conf.first = fna; + conf.last = lna; + conf.password = pwd; + SM.Write("Main", conf); + Log.debug("FirstName in Config: " + conf.first); + } + else + { + fna = conf.first; + lna = conf.last; + pwd = conf.password; + } + + bool startupSeq = true; + + if (File.Exists("ObjectCache.bdf")) File.Delete("ObjectCache.bdf"); + client.Self.ChatFromSimulator += onChatRecv; + client.Self.GroupChatJoined += onJoinGroupChat; + + //client.Objects.ObjectUpdate += onObjectUpdate; + //client.Objects.ObjectProperties += onObjectProperties; + + //client.Network.SimChanged += onSimChange; // Recache prims for this sim + + + //client.Objects.TerseObjectUpdate += onObjectTerseUpdate; + + + client.Settings.OBJECT_TRACKING = true; + client.Settings.ALWAYS_DECODE_OBJECTS = true; + client.Settings.USE_ASSET_CACHE = true; + client.Throttle.Asset = 100000; + client.Throttle.Land = 100000; + client.Throttle.Task = 100000; + client.Throttle.Total = 100000; + + client.Settings.ALWAYS_REQUEST_OBJECTS = true; + + Console.WriteLine("Logging in..."); + + bool LoggedIn = client.Network.Login(fna, lna, pwd, BotStr, BotVer.ToString()); + Console.WriteLine("Logged In: " + LoggedIn.ToString()); + + if (!LoggedIn) + { + Console.WriteLine("Check Creds:\n \nFirst Name: '" + fna + "'\nLast Name: '" + lna + "'\nPWD: '" + pwd + "'\nBotStr: '" + BotStr + "'\nBotVer: " + BotVer.ToString()+"\n \nLogin Message: "+client.Network.LoginMessage); + + + if(args[0] == "-x") // debug launch + Console.ReadKey(); + } + if (LoggedIn) + { + if (File.Exists("XUP")) + { + File.Delete("XUP"); + MH.callbacks(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Updated to version " + BotStr + " - "+BotVer.ToString()); + } + Log.debugf(true, "SL_NET", new[] { "logged_in" }); + + // Setup BotSession Singleton! + BotSession.Instance.grid = client; + BotSession.Instance.Logger = Log; + BotSession.Instance.MHE = MH.callbacks; + BotSession.Instance.MH = MH; + BotSession.Instance.ConfigurationHandle = conf; + while (g_iIsRunning) + { + client.Self.RetrieveInstantMessages(); + if (client.Network.Connected == false) g_iIsRunning = false; // Quit the program and restart immediately! + Thread.Sleep(2000); + DirectoryInfo lp = new DirectoryInfo("update"); + + + + if (lp.Exists) g_iIsRunning = false; + + if (conf.ConfigFor == "Main") + { + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Alert: Main.json is not fully initialized. Setting default values"); + conf.ConfigFor = "BOT"; + conf.ConfigVersion = 1.0f; + // data contains nothing at the moment. + SM.Write("Main", conf); + conf = null; + conf = SM.Read("Main"); + + if (conf.ConfigFor == "BOT") + { + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Main.json has been created"); + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Continuing with startup"); + } + else + { + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Main.json does not contain all memory. FAILURE."); + g_iIsRunning = false; + } + } + else + { + Flavor = conf.ConfigFor; + } + + // Check MainConfiguration for a mainProgram handle + if (conf.MainProgramDLL == null) + { + Log.info("Setting main program library"); + conf.MainProgramDLL = DefaultProgram; + SM.Write("Main", conf); + + } + if (File.Exists(conf.MainProgramDLL) == false) + { + Log.info("MainProgram Library: " + conf.MainProgramDLL + " does not exist"); + if (conf.MainProgramDLL == DefaultProgram) + { + Log.info("FATAL: BlankBot.dll must exist to proceed"); + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "BlankBot.dll does not exist. Please place the blank bot program into the same folder as 'Bot.dll'. Load cannot proceed any further Terminating"); + + } + g_iIsRunning = false; + } + else + { + if (startupSeq) + { + registry = CommandRegistry.Instance; + //ReloadGroupsCache(); + Log.info("MainProgram exists"); + + try + { + int programCount = 0; + PluginActivator PA = new PluginActivator(); + PA.LoadLibrary(conf.MainProgramDLL); + List plugins = PA.Activate(PA.LoadedASM); + + foreach (IProgram plugin in plugins) + { + + plugin.run(client, MH, registry); // simulate constructor and set up other things + g_ZPrograms.Add(plugin); + client.Self.IM += plugin.onIMEvent; + programCount++; + + Log.debug("Plugin: " + plugin.ProgramName + " [" + PA.LoadedASM.FullName + "] added to g_ZPrograms"); + if (File.Exists(plugin.ProgramName + ".bdf")) + plugin.LoadConfiguration(); // will throw an error if BlankBot tries to load config + } + + Log.debug(g_ZPrograms.Count.ToString() + " programs linked"); + if (g_ZPrograms.Count > 0) msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Default Program [" + conf.MainProgramDLL + "] has been loaded, " + programCount.ToString() + " plugin(s) loaded"); + registry.LocateCommands(); + + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Commands found: " + registry.Cmds.Count.ToString()); + + } + catch (Exception E) + { + string Msg = E.Message; + string STACK = E.StackTrace.Replace("ZNI", ""); + Msg = Msg.Replace("ZNI", ""); + Log.debug("Generic Exception Caught: " + Msg + " [0x0A]"); + int i; + int* ptr = &i; + IntPtr addr = (IntPtr)ptr; + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Generic Exception Caught: " + Msg + " [0x0A, 0x" + addr.ToString("x") + "]\nSTACK: " + STACK); + } + } + + + } + foreach (IProgram plugin in g_ZPrograms) + { + plugin.getTick(); // Trigger a tick event!!! + } + + string jsonReply = MH.CheckActions(); + + + if (jsonReply == "NONE") jsonReply = ""; + + + if (jsonReply == "" || jsonReply == null) + { + //Log.debug("TICK NULL"); + + } + else + { + Log.debug("TICK REPLY: " + jsonReply); + dynamic jsonObj = JsonConvert.DeserializeObject(jsonReply); + Log.debug("TYPE: " + jsonObj.type); + string tp = jsonObj.type; + switch (tp) + { + case "assignProgram": + { + + client.Self.Chat("Stand by", 0, ChatType.Normal); + string newProg = jsonObj.newProgram; + if (File.Exists(newProg + ".dll")) + { + conf.MainProgramDLL = jsonObj.newProgram + ".dll"; + SM.Write("Main", conf); + client.Self.Chat("Restarting bot using new main program", 0, ChatType.Normal); + g_iIsRunning = false; + } + else + { + client.Self.Chat("Error: Program '" + newProg + ".dll' does not exist.", 0, ChatType.Normal); + } + break; + } + case "exit": + { + + Log.info("Logging off!"); + g_iIsRunning = false; + break; + } + case "reload_groups": + { + ReloadGroupsCache(); + break; + } + case "load_program": + { + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Stand by.. loading secondary libraries"); + string newProg = jsonObj.newProgram; + if (File.Exists(newProg + ".dll")) + { + newProg += ".dll"; + PluginActivator Plugs = new PluginActivator(); + Plugs.LoadLibrary(newProg); + List libs = Plugs.Activate(Plugs.LoadedASM); + int programCount = 0; + foreach (IProgram plugin in libs) + { + + + plugin.run(client, MH, registry); // simulate constructor and set up other things + g_ZPrograms.Add(plugin); + client.Self.IM += plugin.onIMEvent; + programCount++; + Log.debug("Plugin: " + plugin.ProgramName + " [" + Plugs.LoadedASM.FullName + "] added to g_ZPrograms"); + if (File.Exists(plugin.ProgramName + ".bdf")) + plugin.LoadConfiguration(); // will throw an error if BlankBot tries to load config + } + + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Loaded plugin " + newProg + " with " + programCount.ToString() + " entry points"); + + registry.LocateCommands(); + + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Commands found: " + registry.Cmds.Count.ToString()); + } + else + { + msg(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "ERROR: " + newProg + " could not be located!"); + } + break; + } + default: + { + + Log.debug("Unknown response code"); + break; + } + } + } + + MH.run(client); + //MasterObjectCaches.Save(); + if (startupSeq) startupSeq = false; + + + + //if (MasterObjectCaches.RegionPrims.Count == 0 && client.Network.Connected) + //{ + + // onSimChange(null, new SimChangedEventArgs(client.Network.CurrentSim)); + //} + } + + Log.debugf(false, "SL_NET", new[] { "" }); + + client.Network.Logout(); + } + + + Log.debugf(false, "main", args); + //System.Console.WriteLine("PAUSING. PRESS ANY KEY TO EXIT"); + //System.Console.ReadKey(); + } + + private static void onJoinGroupChat(object sender, GroupChatJoinedEventArgs e) + { + if (e.Success) + MH.GroupJoinWaiter.Set(); + } + + private static AutoResetEvent ReqObjProperties = new AutoResetEvent(false); + private static Dictionary ReqObjPropertiesData = new Dictionary(); + + + [STAThread()] + private static void onObjectUpdate(object sender, PrimEventArgs e) + { + //Console.WriteLine("ObjectUpdate @ " + DateTime.Now); + /* + * Disabled until Libremetaverse is fully tested + * + while (Monitor.IsEntered(_CacheLock)) { } + lock (_CacheLock) + { + + if (MasterObjectCaches == null) + { + MasterObjectCaches = ObjectCaches.Instance; + Console.WriteLine("\n=> Recv: ObjectUpdate; Set: new MasterObjectCache(" + e.Simulator.Name + ")"); + } + if (MasterObjectCaches.RegionPrims == null) MasterObjectCaches.RegionPrims = new Dictionary>(); + Dictionary NewDictionary = new Dictionary(); + if (!MasterObjectCaches.RegionPrims.ContainsKey(e.Simulator.Name)) + { + NewDictionary = new Dictionary(); + try + { + + MasterObjectCaches.RegionPrims.Add(e.Simulator.Name, NewDictionary); + MasterObjectCaches.MarkDirty(); + } + catch (Exception E) + { + Console.WriteLine("FAILED TO INITIALIZE MASTER OBJECT CACHE FOR REGION"); + Console.WriteLine(E.StackTrace); + } + } + else + { + NewDictionary = MasterObjectCaches.RegionPrims[e.Simulator.Name]; + } + Primitive p = e.Prim; + if (!NewDictionary.ContainsKey(e.Prim.ID)) + { + NewDictionary.Add(p.ID, new Primitive2(p)); + MasterObjectCaches.RegionPrims[e.Simulator.Name] = NewDictionary; + MasterObjectCaches.MarkDirty(); + // Check properties val + if (p.Properties == null) + { + client.Objects.SelectObject(client.Network.CurrentSim, p.LocalID); + client.Objects.DeselectObject(client.Network.CurrentSim, p.LocalID); + } + + if (p.OwnerID == client.Self.AgentID) + { + Console.WriteLine("[!] Discovered a prim that i created\n\n"); + } + } + else + { + // Prim is already in list. + // Verify that the properties are still the same + if (p.Properties == null) + { + client.Objects.SelectObject(client.Network.CurrentSim, p.LocalID); + client.Objects.DeselectObject(client.Network.CurrentSim, p.LocalID); + } + + } + } + */ + } + + [STAThread()] + private static void onObjectTerseUpdate(object sender, TerseObjectUpdateEventArgs e) + { + //Console.WriteLine("TerseObjectUpdate @ " + DateTime.Now); + PrimEventArgs ex = new PrimEventArgs(e.Simulator, e.Prim, e.TimeDilation, false, false); + onObjectUpdate(sender, ex); + } + + [STAThread()] + private static void onObjectProperties(object sender, ObjectPropertiesEventArgs e) + { + //Console.WriteLine("ObjectProperties @ " + DateTime.Now); + //Console.WriteLine("\n=> Got prim properties <=\n"); + /* + Dictionary PrimList = MasterObjectCaches.RegionPrims[e.Simulator.Name]; + UUID id = e.Properties.ObjectID; + if (PrimList.ContainsKey(id)) + { + + Primitive2 prim = PrimList[id]; + if (prim.Properties != new Primitive2Properties(e.Properties)) + { + + prim.Properties = new Primitive2Properties(e.Properties); + PrimList[id] = prim; + MasterObjectCaches.RegionPrims[e.Simulator.Name] = PrimList; + MasterObjectCaches.MarkDirty(); + } + } + else + { + // Skip. There is nothing we can do + }*/ + //ReqObjPropertiesData.Add(e.Properties.ObjectID, e.Properties); + // This function is disabled until LibreMetaverse is fully tested + //ReqObjProperties.Set(); + } + + private static void onSimChange(object sender, SimChangedEventArgs e) + { + // Request object data for all prims on sim! + //Dictionary simPrims = client.Network.CurrentSim.ObjectsPrimitives.Copy(); + //ManualResetEvent mreWaiter = new ManualResetEvent(false); + //mreWaiter.Reset(); + //foreach(KeyValuePair kvp in simPrims) + //{ + // onObjectUpdate(null, new PrimEventArgs(client.Network.CurrentSim, kvp.Value, 0, false, false)); + // mreWaiter.WaitOne(TimeSpan.FromMilliseconds(500)); + //} + + } + + private static void onChatRecv(object sender, ChatEventArgs e) + { + if (e.Message == "" || e.Message == "typing") return; + if (e.SourceID == client.Self.AgentID) return; + + string eMe = e.Message; + Dictionary dstuf = new Dictionary(); + Log.debugf(true, "onChatRecv", new[] { e.Message }); + + dstuf.Add("type", "chat"); + string SRC = ""; + if (e.SourceType == ChatSourceType.Agent) SRC = "agent"; + else if (e.SourceType == ChatSourceType.Object) SRC = "obj"; + else if (e.SourceType == ChatSourceType.System) SRC = "sys"; + dstuf.Add("source", SRC); + dstuf.Add("request", eMe); + dstuf.Add("from", e.SourceID.ToString()); + dstuf.Add("from_sess", ""); + dstuf.Add("fromName", e.FromName); + + foreach (IProgram P in g_ZPrograms) + { + Log.debug(JsonConvert.SerializeObject(dstuf)); + Thread X = new Thread(() => P.passArguments(JsonConvert.SerializeObject(dstuf))); + X.Name = "T_" + eMe; + X.Start(); + } + Log.debugf(false, "onChatRecv", new[] { "" }); + + } + + + + + + + + + + + + private static Dictionary GroupsCache = null; + private static ManualResetEvent GroupsEvent = new ManualResetEvent(false); + private static void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e) + { + if (null == GroupsCache) + GroupsCache = e.Groups; + else + lock (GroupsCache) { GroupsCache = e.Groups; } + GroupsEvent.Set(); + } + private static void ReloadGroupsCache() + { + client.Groups.CurrentGroups += Groups_CurrentGroups; + client.Groups.RequestCurrentGroups(); + GroupsEvent.WaitOne(10000, false); + client.Groups.CurrentGroups -= Groups_CurrentGroups; + GroupsEvent.Reset(); + } + + private static UUID GroupName2UUID(String groupName) + { + UUID tryUUID; + if (UUID.TryParse(groupName, out tryUUID)) + return tryUUID; + if (null == GroupsCache) + { + ReloadGroupsCache(); + if (null == GroupsCache) + return UUID.Zero; + } + lock (GroupsCache) + { + if (GroupsCache.Count > 0) + { + foreach (Group currentGroup in GroupsCache.Values) + if (currentGroup.Name.ToLower() == groupName.ToLower()) + return currentGroup.ID; + } + } + return UUID.Zero; + } + } + + public class Tools + { + public static Int32 getTimestamp() + { + return int.Parse(DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()); + } + + public static string Hash2String(byte[] Hash) + { + StringBuilder sb = new StringBuilder(); + foreach(byte b in Hash) + { + sb.Append(b.ToString("X2")); + } + return sb.ToString(); + } + + public static string MD5Hash(string ToHash) + { + byte[] Source = UTF8Encoding.UTF8.GetBytes(ToHash); + byte[] Hash = new MD5CryptoServiceProvider().ComputeHash(Source); + return Tools.Hash2String(Hash); + } + + public static string MD5Hash(byte[] ToHash) + { + return Tools.Hash2String(new MD5CryptoServiceProvider().ComputeHash(ToHash)); + } + + public static string SHA256Hash(string ToHash) + { + SHA256 hasher = SHA256.Create(); + return Tools.Hash2String(hasher.ComputeHash(UTF8Encoding.UTF8.GetBytes(ToHash))); + } + + public static string SHA256Hash(byte[] ToHash) + { + SHA256 Hasher = SHA256.Create(); + return Tools.Hash2String(Hasher.ComputeHash(ToHash)); + } + } +} diff --git a/Source/SerialManager.cs b/Source/SerialManager.cs new file mode 100644 index 0000000..7ea50d8 --- /dev/null +++ b/Source/SerialManager.cs @@ -0,0 +1,114 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.Serialization; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using Newtonsoft.Json; + + + +namespace Bot +{ + public class SerialManager // Handles saving a large amount of data to a binary file or vise-versa + { + /* + public void Write(string Name, T ObjectData) + { + Stream F = null; + BinaryFormatter BinaryFormat = new BinaryFormatter(); + try + { + if (File.Exists(Name + ".bdf")) File.Copy(Name + ".bdf", Name + ".bdf.bak", true); + F = new FileStream(Name + ".bdf", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); + BinaryFormat.Serialize(F, ObjectData); + } + catch (SerializationException e) + { + Console.WriteLine(e.Message); + } + F.Close(); + } + + public T Read(string Name) + { + + if (File.Exists(Name + ".bdf") == false) throw new FileNotFoundException(); + + Stream F = new FileStream(Name + ".bdf", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + BinaryFormatter BinaryFormat = new BinaryFormatter(); + T deserial = default(T); + try + { + deserial = (T)BinaryFormat.Deserialize(F); + } + catch (SerializationException e) + { + Console.WriteLine(e.Message); + } + catch (Exception e) + { + // + Console.WriteLine(e.Message); + } + + F.Close(); + if (deserial == null) deserial = default(T); + + Console.WriteLine("Returning deserialized class"); + return deserial; + } + + */ + private static readonly object _fileAccess = new object(); + public void Write(string Name, T ObjectData) + { + string Json = JsonConvert.SerializeObject(ObjectData, Formatting.Indented); + lock (_fileAccess) + { + + try + { + File.WriteAllText(Name + ".json", Json); + } catch(Exception E) + { + Console.WriteLine(E.Message); + } + } + + } + + public T Read (string Name) + { + lock(_fileAccess){ + + try + { + + T obj = default(T); + string serial = File.ReadAllText(Name + ".json"); + + obj = (T)JsonConvert.DeserializeObject(serial); + Console.WriteLine("Returning class object"); + + if (obj == null) obj = default(T); + return obj; + } + catch (Exception e) + { + Console.WriteLine(e.Message); + throw new FileNotFoundException(); + } + } + + } + } +} diff --git a/Source/SysOut.cs b/Source/SysOut.cs new file mode 100644 index 0000000..4341198 --- /dev/null +++ b/Source/SysOut.cs @@ -0,0 +1,144 @@ +/* +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the AGPL-3.0 +*/ + + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bot +{ + public sealed class SysOut + { + + private static SysOut _Inst = null; + private static readonly object instloc = new object(); + + static SysOut() { } + + public static SysOut Instance + { + get + { + lock (instloc) + { + if(_Inst == null) + { + _Inst = new SysOut(); + _Inst.FLAVOR = "Bot"; + } + return _Inst; + } + } + } + public int tabs = 0; + string FLAVOR; + + private static readonly object Locks = new object(); + + + public void info(string msg) + { + lock (Locks) + { + + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("["); + Console.ForegroundColor = ConsoleColor.Green; + System.Console.Write("INFO"); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("] ["); + Console.ForegroundColor = ConsoleColor.Red; + System.Console.Write(FLAVOR); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("] "); + Console.ForegroundColor = ConsoleColor.Cyan; + for (int i = 0; i < tabs; i++) { Console.Write("\t|"); } + System.Console.Write(msg); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("\n"); + } + } + + public void debugf(bool enter, string label, string[] debugParams) + { + lock (Locks) + { + + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("["); + Console.ForegroundColor = ConsoleColor.DarkRed; + System.Console.Write("DEBUG"); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("] ["); + Console.ForegroundColor = ConsoleColor.Green; + System.Console.Write(FLAVOR); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("] "); + Console.ForegroundColor = ConsoleColor.Magenta; + + if (enter) + { + for (int i = 0; i < tabs; i++) { System.Console.Write("\t"); } + System.Console.Write("ENTER "); + tabs++; + Console.BackgroundColor = ConsoleColor.Cyan; + System.Console.Write(label); + Console.BackgroundColor = ConsoleColor.Black; + Console.ForegroundColor = ConsoleColor.Red; + Console.Write(" "); + } + else + { + tabs--; + for (int i = 0; i < tabs; i++) { System.Console.Write("\t"); } + System.Console.Write("LEAVE "); + Console.BackgroundColor = ConsoleColor.Cyan; + System.Console.Write(label); + Console.BackgroundColor = ConsoleColor.Black; + Console.ForegroundColor = ConsoleColor.Red; + Console.Write(" "); + } + Console.Write("["); + for (int i = 0; i < debugParams.Length; i++) + { + Console.Write(debugParams[i] + ", "); + } + Console.Write("]"); + + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("\n"); + Console.ResetColor(); + } + } + + public void debug(string m) + { + lock (Locks) + { + + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("["); + Console.ForegroundColor = ConsoleColor.DarkRed; + System.Console.Write("DEBUG"); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("] ["); + Console.ForegroundColor = ConsoleColor.Green; + System.Console.Write(FLAVOR); + Console.ForegroundColor = ConsoleColor.White; + System.Console.Write("] "); + Console.ForegroundColor = ConsoleColor.Magenta; + + for (int i = 0; i < tabs; i++) + { + Console.Write("\t|"); + } + Console.Write(" " + m); + Console.Write("\n"); + } + + } + } +} \ No newline at end of file