diff --git a/MinecraftClient/Bots.cs b/MinecraftClient/Bots.cs
deleted file mode 100644
index 8260fcb6..00000000
--- a/MinecraftClient/Bots.cs
+++ /dev/null
@@ -1,1167 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Globalization;
-
-namespace MinecraftClient
-{
- ///
- /// Welcome to the Bot API file !
- /// The virtual class "ChatBot" contains anything you need for creating chat bots
- /// Inherit from this class while adding your bot class to the namespace "Bots", below.
- /// Once your bot is created, read the explanations below to start using it in the MinecraftClient app.
- ///
- /// Pieces of code to add in other parts of the program for your bot. Line numbers are approximative.
- /// Program.cs:166 | if (Settings.YourBot_Enabled){ handler.BotLoad(new Bots.YourBot()); }
- /// Settings.cs:73 | public static bool YourBot_Enabled = false;
- /// Settings.cs:74 | private enum ParseMode { /* [...] */, YourBot };
- /// Settings.cs:106| case "yourbot": pMode = ParseMode.YourBot; break;
- /// Settings.cs:197| case ParseMode.YourBot: switch (argName.ToLower()) { case "enabled": YourBot_Enabled = str2bool(argValue); break; } break;
- /// Settings.cs:267| + "[YourBot]\r\n" + "enabled=false\r\n"
- /// Here your are. Now you will have a setting in MinecraftClient.ini for enabling your brand new bot.
- /// Delete MinecraftClient.ini to re-generate it or add the lines [YourBot] and enabled=true to the existing one.
- ///
-
- ///
- /// The virtual class containing anything you need for creating chat bots.
- ///
-
- public abstract class ChatBot
- {
- public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost };
-
- #region MinecraftCom Handler for this bot
-
- //Will be automatically set on bot loading, don't worry about this
- public void SetHandler(MinecraftCom handler) { this.handler = handler; }
- private MinecraftCom handler;
-
- #endregion
-
- ///
- /// Anything you want to initialize your bot, will be called on load by MinecraftCom
- ///
-
- public virtual void Initialize() { }
-
- ///
- /// Will be called every ~100ms (10fps) if loaded in MinecraftCom
- ///
-
- public virtual void Update() { }
-
- ///
- /// Any text sent by the server will be sent here by MinecraftCom
- ///
- /// Text from the server
-
- public virtual void GetText(string text) { }
-
- ///
- /// Is called when the client has been disconnected fom the server
- ///
- /// Disconnect Reason
- /// Kick message, if any
- /// Return TRUE if the client is about to restart
-
- public virtual bool OnDisconnect(DisconnectReason reason, string message) { return false; }
-
- #region ToolBox
-
- ///
- /// Send text to the server. Can be anything such as chat messages or commands
- ///
- /// Text to send to the server
-
- protected void SendText(string text)
- {
- Console.ForegroundColor = ConsoleColor.DarkGray;
- ConsoleIO.WriteLine("BOT:" + text);
- handler.SendChatMessage(text);
- Console.ForegroundColor = ConsoleColor.Gray;
- }
-
- ///
- /// Remove color codes ("§c") from a text message received from the server
- ///
-
- protected static string getVerbatim(string text)
- {
- if ( String.IsNullOrEmpty(text) )
- return String.Empty;
-
- int idx = 0;
- var data = new char[text.Length];
-
- for ( int i = 0; i < text.Length; i++ )
- if ( text[i] != '§' )
- data[idx++] = text[i];
- else
- i++;
-
- return new string(data, 0, idx);
- }
-
- ///
- /// Verify that a string contains only a-z A-Z 0-9 and _ characters.
- ///
-
- protected static bool isValidName(string username)
- {
- if ( String.IsNullOrEmpty(username) )
- return false;
-
- foreach ( char c in username )
- if ( !((c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9')
- || c == '_') )
- return false;
-
- return true;
- }
-
- ///
- /// Returns true is the text passed is a private message sent to the bot
- ///
- /// text to test
- /// if it's a private message, this will contain the message
- /// if it's a private message, this will contain the player name that sends the message
- /// Returns true if the text is a private message
-
- protected static bool isPrivateMessage(string text, ref string message, ref string sender)
- {
- if (text == "") { return false; }
- string[] tmp = text.Split(' ');
-
- try
- {
- //Detect vanilla /tell messages
- //Someone whispers to you: message
- if (tmp.Length > 2 && tmp[1] == "whispers")
- {
- message = text.Substring(tmp[0].Length + 18);
- sender = tmp[0];
- return isValidName(sender);
- }
-
- //Detect Essentials (Bukkit) /m messages
- //[Someone -> me] message
- else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "->"
- && (tmp[2] == "me]" || tmp[2] == "moi]")) //'me' is replaced by 'moi' in french servers
- {
- message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 1);
- sender = tmp[0].Substring(1);
- if (sender[0] == '~') { sender = sender.Substring(1); }
- return isValidName(sender);
- }
- else return false;
- }
- catch (IndexOutOfRangeException) { return false; }
- }
-
- ///
- /// Returns true is the text passed is a public message written by a player on the chat
- ///
- /// text to test
- /// if it's message, this will contain the message
- /// if it's message, this will contain the player name that sends the message
- /// Returns true if the text is a chat message
-
- protected static bool isChatMessage(string text, ref string message, ref string sender)
- {
- //Detect chat messages
- // message
- //<*Faction Someone> message
- //<*Faction Someone>: message
- //<*Faction ~Nicknamed>: message
- if (text == "") { return false; }
- if (text[0] == '<')
- {
- try
- {
- text = text.Substring(1);
- string[] tmp = text.Split('>');
- sender = tmp[0];
- message = text.Substring(sender.Length + 2);
- if (message.Length > 1 && message[0] == ' ')
- { message = message.Substring(1); }
- tmp = sender.Split(' ');
- sender = tmp[tmp.Length - 1];
- if (sender[0] == '~') { sender = sender.Substring(1); }
- return isValidName(sender);
- }
- catch (IndexOutOfRangeException) { return false; }
- }
- else return false;
- }
-
- ///
- /// Writes some text in the console. Nothing will be sent to the server.
- ///
- /// Log text to write
-
- public static void LogToConsole(string text)
- {
- Console.ForegroundColor = ConsoleColor.DarkGray;
- ConsoleIO.WriteLine("[BOT] " + text);
- Console.ForegroundColor = ConsoleColor.Gray;
- }
-
- ///
- /// Disconnect from the server and restart the program
- /// It will unload & reload all the bots and then reconnect to the server
- ///
-
- protected void ReconnectToTheServer() { ReconnectToTheServer(3); }
-
- ///
- /// Disconnect from the server and restart the program
- /// It will unload & reload all the bots and then reconnect to the server
- ///
- /// If connection fails, the client will make X extra attempts
-
- protected void ReconnectToTheServer(int ExtraAttempts)
- {
- McTcpClient.AttemptsLeft = ExtraAttempts;
- Program.Restart();
- }
-
- ///
- /// Disconnect from the server and exit the program
- ///
-
- protected void DisconnectAndExit()
- {
- Program.Exit();
- }
-
- ///
- /// Unload the chatbot, and release associated memory.
- ///
-
- protected void UnloadBot()
- {
- handler.BotUnLoad(this);
- }
-
- ///
- /// Send a private message to a player
- ///
- /// Player name
- /// Message
-
- protected void SendPrivateMessage(string player, string message)
- {
- SendText("/tell " + player + ' ' + message);
- }
-
- ///
- /// Run a script from a file using a Scripting bot
- ///
- /// File name
- /// Player name to send error messages, if applicable
-
- protected void RunScript(string filename, string playername = "")
- {
- handler.BotLoad(new Bots.Script(filename, playername));
- }
-
- #endregion
- }
-
- namespace Bots
- {
- ///
- /// Example of message receiving.
- ///
-
- public class TestBot : ChatBot
- {
- public override void GetText(string text)
- {
- string message = "";
- string username = "";
- text = getVerbatim(text);
-
- if (isPrivateMessage(text, ref message, ref username))
- {
- ConsoleIO.WriteLine("Bot: " + username + " told me : " + message);
- }
- else if (isChatMessage(text, ref message, ref username))
- {
- ConsoleIO.WriteLine("Bot: " + username + " said : " + message);
- }
- }
- }
-
- ///
- /// This bot sends a command every 60 seconds in order to stay non-afk.
- ///
-
- public class AntiAFK : ChatBot
- {
- private int count;
- private int timeping;
-
- ///
- /// This bot sends a /ping command every X seconds in order to stay non-afk.
- ///
- /// Time amount between each ping (10 = 1s, 600 = 1 minute, etc.)
-
- public AntiAFK(int pingparam)
- {
- count = 0;
- timeping = pingparam;
- if (timeping < 10) { timeping = 10; } //To avoid flooding
- }
-
- public override void Update()
- {
- count++;
- if (count == timeping)
- {
- SendText(Settings.AntiAFK_Command);
- count = 0;
- }
- }
- }
-
- ///
- /// This bot sends a /list command every X seconds and save the result.
- ///
-
- public class PlayerListLogger : ChatBot
- {
- private int count;
- private int timeping;
- private string file;
-
- ///
- /// This bot sends a /list command every X seconds and save the result.
- ///
- /// Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.)
-
- public PlayerListLogger(int pingparam, string filetosavein)
- {
- count = 0;
- file = filetosavein;
- timeping = pingparam;
- if (timeping < 10) { timeping = 10; } //To avoid flooding
-
- }
-
- public override void Update()
- {
- count++;
- if (count == timeping)
- {
- SendText("/list");
- count = 0;
- }
- }
-
- public override void GetText(string text)
- {
- if (text.Contains("Joueurs en ligne") || text.Contains("Connected:") || text.Contains("online:"))
- {
- LogToConsole("Saving Player List");
- DateTime now = DateTime.Now;
- string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']';
- System.IO.File.AppendAllText(file, TimeStamp + "\n" + getVerbatim(text) + "\n\n");
- }
- }
- }
-
- ///
- /// "Le jeu du Pendu" (Hangman game)
- ///
-
- public class Pendu : ChatBot
- {
- private int vie = 0;
- private int vie_param = 10;
- private int compteur = 0;
- private int compteur_param = 3000; //5 minutes
- private bool running = false;
- private bool[] discovered;
- private string word = "";
- private string letters = "";
- private bool English;
-
- ///
- /// "Le jeu du Pendu" (Hangman Game)
- ///
- /// if true, the game will be in english. If false, the game will be in french.
-
- public Pendu(bool english)
- {
- English = english;
- }
-
- public override void Update()
- {
- if (running)
- {
- if (compteur > 0)
- {
- compteur--;
- }
- else
- {
- SendText(English ? "You took too long to try a letter." : "Temps imparti écoulé !");
- SendText(English ? "Game canceled." : "Partie annulée.");
- running = false;
- }
- }
- }
-
- public override void GetText(string text)
- {
- string message = "";
- string username = "";
- text = getVerbatim(text);
-
- if (isPrivateMessage(text, ref message, ref username))
- {
- if (Settings.Bots_Owners.Contains(username.ToLower()))
- {
- switch (message)
- {
- case "start":
- start();
- break;
- case "stop":
- running = false;
- break;
- default:
- break;
- }
- }
- }
- else
- {
- if (running && isChatMessage(text, ref message, ref username))
- {
- if (message.Length == 1)
- {
- char letter = message.ToUpper()[0];
- if (letter >= 'A' && letter <= 'Z')
- {
- if (letters.Contains(letter))
- {
- SendText(English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé."));
- }
- else
- {
- letters += letter;
- compteur = compteur_param;
-
- if (word.Contains(letter))
- {
- for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { discovered[i] = true; } }
- SendText(English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)"));
- }
- else
- {
- vie--;
- if (vie == 0)
- {
- SendText(English ? "Game Over! :]" : "Perdu ! Partie terminée :]");
- SendText(English ? ("The word was: " + word) : ("Le mot était : " + word));
- running = false;
- }
- else SendText(English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non."));
- }
-
- if (running)
- {
- SendText(English ? ("Mysterious word: " + word_cached + " (lives : " + vie + ")")
- : ("Mot mystère : " + word_cached + " (vie : " + vie + ")"));
- }
-
- if (winner)
- {
- SendText(English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !"));
- running = false;
- }
- }
- }
- }
- }
- }
- }
-
- private void start()
- {
- vie = vie_param;
- running = true;
- letters = "";
- word = chooseword();
- compteur = compteur_param;
- discovered = new bool[word.Length];
-
- SendText(English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio");
- SendText(English ? ("Mysterious word: " + word_cached + " (lives : " + vie + ")")
- : ("Mot mystère : " + word_cached + " (vie : " + vie + ")"));
- SendText(English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)"));
- }
-
- private string chooseword()
- {
- if (System.IO.File.Exists(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR))
- {
- string[] dico = System.IO.File.ReadAllLines(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR);
- return dico[new Random().Next(dico.Length)];
- }
- else
- {
- LogToConsole(English ? "File not found: " + Settings.Hangman_FileWords_EN : "Fichier introuvable : " + Settings.Hangman_FileWords_FR);
- return English ? "WORDSAREMISSING" : "DICOMANQUANT";
- }
- }
-
- private string word_cached
- {
- get
- {
- string printed = "";
- for (int i = 0; i < word.Length; i++)
- {
- if (discovered[i])
- {
- printed += word[i];
- }
- else printed += '_';
- }
- return printed;
- }
- }
-
- private bool winner
- {
- get
- {
- for (int i = 0; i < discovered.Length; i++)
- {
- if (!discovered[i])
- {
- return false;
- }
- }
- return true;
- }
- }
- }
-
- ///
- /// This bot make the console beep on some specified words. Useful to detect when someone is talking to you, for example.
- ///
-
- public class Alerts : ChatBot
- {
- private string[] dictionary = new string[0];
- private string[] excludelist = new string[0];
-
- public override void Initialize()
- {
- if (System.IO.File.Exists(Settings.Alerts_MatchesFile))
- {
- dictionary = System.IO.File.ReadAllLines(Settings.Alerts_MatchesFile);
-
- for (int i = 0; i < dictionary.Length; i++)
- {
- dictionary[i] = dictionary[i].ToLower();
- }
- }
- else LogToConsole("File not found: " + Settings.Alerts_MatchesFile);
-
- if (System.IO.File.Exists(Settings.Alerts_ExcludesFile))
- {
- excludelist = System.IO.File.ReadAllLines(Settings.Alerts_ExcludesFile);
-
- for (int i = 0; i < excludelist.Length; i++)
- {
- excludelist[i] = excludelist[i].ToLower();
- }
- }
- else LogToConsole("File not found : " + Settings.Alerts_ExcludesFile);
- }
-
- public override void GetText(string text)
- {
- text = getVerbatim(text);
- string comp = text.ToLower();
- foreach (string alert in dictionary)
- {
- if (comp.Contains(alert))
- {
- bool ok = true;
-
- foreach (string exclusion in excludelist)
- {
- if (comp.Contains(exclusion))
- {
- ok = false;
- break;
- }
- }
-
- if (ok)
- {
- if (Settings.Alerts_Beep_Enabled) { Console.Beep(); } //Text found !
-
- if (ConsoleIO.basicIO) { ConsoleIO.WriteLine(comp.Replace(alert, "§c" + alert + "§r")); } else {
-
- #region Displaying the text with the interesting part highlighted
-
- Console.BackgroundColor = ConsoleColor.DarkGray;
- Console.ForegroundColor = ConsoleColor.White;
-
- //Will be used for text displaying
- string[] temp = comp.Split(alert.Split(','), StringSplitOptions.RemoveEmptyEntries);
- int p = 0;
-
- //Special case : alert in the beginning of the text
- string test = "";
- for (int i = 0; i < alert.Length; i++)
- {
- test += comp[i];
- }
- if (test == alert)
- {
- Console.BackgroundColor = ConsoleColor.Yellow;
- Console.ForegroundColor = ConsoleColor.Red;
- for (int i = 0; i < alert.Length; i++)
- {
- ConsoleIO.Write(text[p]);
- p++;
- }
- }
-
- //Displaying the rest of the text
- for (int i = 0; i < temp.Length; i++)
- {
- Console.BackgroundColor = ConsoleColor.DarkGray;
- Console.ForegroundColor = ConsoleColor.White;
- for (int j = 0; j < temp[i].Length; j++)
- {
- ConsoleIO.Write(text[p]);
- p++;
- }
- Console.BackgroundColor = ConsoleColor.Yellow;
- Console.ForegroundColor = ConsoleColor.Red;
- try
- {
- for (int j = 0; j < alert.Length; j++)
- {
- ConsoleIO.Write(text[p]);
- p++;
- }
- }
- catch (IndexOutOfRangeException) { }
- }
- Console.BackgroundColor = ConsoleColor.Black;
- Console.ForegroundColor = ConsoleColor.Gray;
- ConsoleIO.Write('\n');
-
- #endregion
-
- }
- }
- }
- }
- }
- }
-
- ///
- /// This bot saves the received messages in a text file.
- ///
-
- public class ChatLog : ChatBot
- {
- public enum MessageFilter { AllText, AllMessages, OnlyChat, OnlyWhispers };
- private bool dateandtime;
- private bool saveOther = true;
- private bool saveChat = true;
- private bool savePrivate = true;
- private string logfile;
-
- ///
- /// This bot saves the messages received in the specified file, with some filters and date/time tagging.
- ///
- /// The file to save the log in
- /// The kind of messages to save
- /// Add a date and time before each message
-
- public ChatLog(string file, MessageFilter filter, bool AddDateAndTime)
- {
- dateandtime = AddDateAndTime;
- logfile = file;
- switch (filter)
- {
- case MessageFilter.AllText:
- saveOther = true;
- savePrivate = true;
- saveChat = true;
- break;
- case MessageFilter.AllMessages:
- saveOther = false;
- savePrivate = true;
- saveChat = true;
- break;
- case MessageFilter.OnlyChat:
- saveOther = false;
- savePrivate = false;
- saveChat = true;
- break;
- case MessageFilter.OnlyWhispers:
- saveOther = false;
- savePrivate = true;
- saveChat = false;
- break;
- }
- }
-
- public static MessageFilter str2filter(string filtername)
- {
- switch (filtername.ToLower())
- {
- case "all": return MessageFilter.AllText;
- case "messages": return MessageFilter.AllMessages;
- case "chat": return MessageFilter.OnlyChat;
- case "private": return MessageFilter.OnlyWhispers;
- default: return MessageFilter.AllText;
- }
- }
-
- public override void GetText(string text)
- {
- text = getVerbatim(text);
- string sender = "";
- string message = "";
-
- if (saveChat && isChatMessage(text, ref message, ref sender))
- {
- save("Chat " + sender + ": " + message);
- }
- else if (savePrivate && isPrivateMessage(text, ref message, ref sender))
- {
- save("Private " + sender + ": " + message);
- }
- else if (saveOther)
- {
- save("Other: " + text);
- }
- }
-
- private void save(string tosave)
- {
- if (dateandtime)
- {
- int day = DateTime.Now.Day, month = DateTime.Now.Month;
- int hour = DateTime.Now.Hour, minute = DateTime.Now.Minute, second = DateTime.Now.Second;
-
- string D = day < 10 ? "0" + day : "" + day;
- string M = month < 10 ? "0" + month : "" + day;
- string Y = "" + DateTime.Now.Year;
-
- string h = hour < 10 ? "0" + hour : "" + hour;
- string m = minute < 10 ? "0" + minute : "" + minute;
- string s = second < 10 ? "0" + second : "" + second;
-
- tosave = "" + D + '-' + M + '-' + Y + ' ' + h + ':' + m + ':' + s + ' ' + tosave;
- }
-
- System.IO.FileStream stream = new System.IO.FileStream(logfile, System.IO.FileMode.OpenOrCreate);
- System.IO.StreamWriter writer = new System.IO.StreamWriter(stream);
- stream.Seek(0, System.IO.SeekOrigin.End);
- writer.WriteLine(tosave);
- writer.Dispose();
- stream.Close();
- }
- }
-
- ///
- /// This bot automatically re-join the server if kick message contains predefined string (Server is restarting ...)
- ///
-
- public class AutoRelog : ChatBot
- {
- private string[] dictionary = new string[0];
- private int attempts;
- private int delay;
-
- ///
- /// This bot automatically re-join the server if kick message contains predefined string
- ///
- /// Delay before re-joining the server (in seconds)
- /// Number of retries if connection fails (-1 = infinite)
-
- public AutoRelog(int DelayBeforeRelog, int retries)
- {
- attempts = retries;
- if (attempts == -1) { attempts = int.MaxValue; }
- McTcpClient.AttemptsLeft = attempts;
- delay = DelayBeforeRelog;
- if (delay < 1) { delay = 1; }
- }
-
- public override void Initialize()
- {
- McTcpClient.AttemptsLeft = attempts;
- if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile))
- {
- dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile);
-
- for (int i = 0; i < dictionary.Length; i++)
- {
- dictionary[i] = dictionary[i].ToLower();
- }
- }
- else LogToConsole("File not found: " + Settings.AutoRelog_KickMessagesFile);
- }
-
- public override bool OnDisconnect(DisconnectReason reason, string message)
- {
- message = getVerbatim(message);
- string comp = message.ToLower();
- foreach (string msg in dictionary)
- {
- if (comp.Contains(msg))
- {
- LogToConsole("Waiting " + delay + " seconds before reconnecting...");
- System.Threading.Thread.Sleep(delay * 1000);
- McTcpClient.AttemptsLeft = attempts;
- ReconnectToTheServer();
- return true;
- }
- }
- return false;
- }
- }
-
- ///
- /// Runs a list of commands
- ///
-
- public class Script : ChatBot
- {
- private string file;
- private string[] lines = new string[0];
- private int sleepticks = 10;
- private int sleepticks_interval = 10;
- private int nextline = 0;
- private string owner;
-
- public Script(string filename)
- {
- file = filename;
- }
-
- public Script(string filename, string ownername)
- :this(filename)
- {
- if (ownername != "")
- owner = ownername;
- }
-
- public static bool lookForScript(ref string filename)
- {
- //Automatically look in subfolders and try to add ".txt" file extension
- string[] files = new string[]
- {
- filename,
- filename + ".txt",
- "scripts\\" + filename,
- "scripts\\" + filename + ".txt",
- "config\\" + filename,
- "config\\" + filename + ".txt",
- };
-
- foreach (string possible_file in files)
- {
- if (System.IO.File.Exists(possible_file))
- {
- filename = possible_file;
- return true;
- }
- }
-
- return false;
- }
-
- public override void Initialize()
- {
- //Load the given file from the startup parameters
- if (lookForScript(ref file))
- {
- lines = System.IO.File.ReadAllLines(file);
- if (owner != null) { SendPrivateMessage(owner, "Script '" + file + "' loaded."); }
- }
- else
- {
- LogToConsole("File not found: '" + file + "'");
- if (owner != null)
- SendPrivateMessage(owner, "File not found: '" + file + "'");
- UnloadBot(); //No need to keep the bot active
- }
- }
-
- public override void Update()
- {
- if (sleepticks > 0) { sleepticks--; }
- else
- {
- if (nextline < lines.Length) //Is there an instruction left to interpret?
- {
- string instruction_line = lines[nextline].Trim(); // Removes all whitespaces at start and end of current line
- nextline++; //Move the cursor so that the next time the following line will be interpreted
- sleepticks = sleepticks_interval; //Used to delay next command sending and prevent from beign kicked for spamming
-
- if (instruction_line.Length > 1)
- {
- if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
- {
- string instruction_name = instruction_line.Split(' ')[0];
- switch (instruction_name.ToLower())
- {
- case "send":
- SendText(instruction_line.Substring(5, instruction_line.Length - 5));
- break;
- case "wait":
- int ticks = 10;
- try
- {
- ticks = Convert.ToInt32(instruction_line.Substring(5, instruction_line.Length - 5));
- }
- catch { }
- sleepticks = ticks;
- break;
- case "disconnect":
- DisconnectAndExit();
- break;
- case "exit": //Exit bot & stay connected to the server
- UnloadBot();
- break;
- case "connect":
- if (instruction_line.Length >= 9)
- {
- Settings.ServerIP = instruction_line.Substring(8);
- ReconnectToTheServer();
- }
- break;
- default:
- sleepticks = 0; Update(); //Unknown command : process next line immediately
- break;
- }
- }
- else { sleepticks = 0; Update(); } //Comment: process next line immediately
- }
- }
- else
- {
- //No more instructions to interpret
- UnloadBot();
- }
- }
- }
- }
-
- ///
- /// Trigger scripts on specific events
- ///
-
- public class ScriptScheduler : ChatBot
- {
- private class TaskDesc
- {
- public string script_file = null;
- public bool triggerOnFirstLogin = false;
- public bool triggerOnLogin = false;
- public bool triggerOnTime = false;
- public List triggerOnTime_Times = new List();
- public bool alreadyTriggered = false;
- }
-
- private static bool firstlogin_done = false;
-
- private string tasksfile;
- private bool serverlogin_done;
- private List tasks = new List();
- private int verifytasks_timeleft = 10;
- private int verifytasks_delay = 10;
-
- public ScriptScheduler(string tasksfile)
- {
- this.tasksfile = tasksfile;
- serverlogin_done = false;
- }
-
- public override void Initialize()
- {
- //Load the given file from the startup parameters
- if (System.IO.File.Exists(tasksfile))
- {
- TaskDesc current_task = null;
- String[] lines = System.IO.File.ReadAllLines(tasksfile);
- foreach (string lineRAW in lines)
- {
- string line = lineRAW.Split('#')[0].Trim();
- if (line.Length > 0)
- {
- if (line[0] == '[' && line[line.Length - 1] == ']')
- {
- switch (line.Substring(1, line.Length - 2).ToLower())
- {
- case "task":
- checkAddTask(current_task);
- current_task = new TaskDesc(); //Create a blank task
- break;
- }
- }
- else if (current_task != null)
- {
- string argName = line.Split('=')[0];
- if (line.Length > (argName.Length + 1))
- {
- string argValue = line.Substring(argName.Length + 1);
- switch (argName.ToLower())
- {
- case "triggeronfirstlogin": current_task.triggerOnFirstLogin = Settings.str2bool(argValue); break;
- case "triggeronlogin": current_task.triggerOnLogin = Settings.str2bool(argValue); break;
- case "triggerontime": current_task.triggerOnTime = Settings.str2bool(argValue); break;
- case "timevalue": try { current_task.triggerOnTime_Times.Add(DateTime.ParseExact(argValue, "HH:mm", CultureInfo.InvariantCulture)); } catch { } break;
- case "script": current_task.script_file = argValue; break;
- }
- }
- }
- }
- }
- checkAddTask(current_task);
- }
- else
- {
- LogToConsole("File not found: '" + tasksfile + "'");
- UnloadBot(); //No need to keep the bot active
- }
- }
-
- private void checkAddTask(TaskDesc current_task)
- {
- if (current_task != null)
- {
- //Check if we built a valid task before adding it
- if (current_task.script_file != null && Script.lookForScript(ref current_task.script_file) //Check if file exists
- && (current_task.triggerOnLogin || (current_task.triggerOnTime && current_task.triggerOnTime_Times.Count > 0))) //Look for a valid trigger
- {
- tasks.Add(current_task);
- }
- }
- }
-
- public override void Update()
- {
- if (verifytasks_timeleft <= 0)
- {
- verifytasks_timeleft = verifytasks_delay;
- if (serverlogin_done)
- {
- foreach (TaskDesc task in tasks)
- {
- if (task.triggerOnTime)
- {
- foreach (DateTime time in task.triggerOnTime_Times)
- {
- if (time.Hour == DateTime.Now.Hour && time.Minute == DateTime.Now.Minute)
- {
- if (!task.alreadyTriggered)
- {
- task.alreadyTriggered = true;
- RunScript(task.script_file);
- }
- }
- }
- }
- else task.alreadyTriggered = false;
- }
- }
- else
- {
- foreach (TaskDesc task in tasks)
- {
- if (task.triggerOnLogin || (firstlogin_done == false && task.triggerOnFirstLogin))
- RunScript(task.script_file);
- }
-
- firstlogin_done = true;
- serverlogin_done = true;
- }
- }
- else verifytasks_timeleft--;
- }
- }
-
- ///
- /// Allow to perform operations using whispers to the bot
- ///
-
- public class RemoteControl : ChatBot
- {
- public override void GetText(string text)
- {
- text = getVerbatim(text);
- string command = "", sender = "";
- if (isPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower()))
- {
- string cmd_name = command.Split(' ')[0];
- switch (cmd_name.ToLower())
- {
- case "exit":
- DisconnectAndExit();
- break;
- case "reco":
- ReconnectToTheServer();
- break;
- case "script":
- if (command.Length >= 8)
- RunScript(command.Substring(7), sender);
- break;
- case "send":
- if (command.Length >= 6)
- SendText(command.Substring(5));
- break;
- case "connect":
- if (command.Length >= 9)
- {
- Settings.ServerIP = command.Substring(8);
- ReconnectToTheServer();
- }
- break;
- case "help":
- if (command.Length >= 6)
- {
- string help_cmd_name = command.Substring(5).ToLower();
- switch (help_cmd_name)
- {
- case "exit": SendPrivateMessage(sender, "exit: disconnect from the server."); break;
- case "reco": SendPrivateMessage(sender, "reco: restart and reconnct to the server."); break;
- case "script": SendPrivateMessage(sender, "script : run a script file."); break;
- case "send": SendPrivateMessage(sender, "send : send a chat message or command."); break;
- case "connect": SendPrivateMessage(sender, "connect : connect to the specified server."); break;
- case "help": SendPrivateMessage(sender, "help : show brief help about a command."); break;
- default: SendPrivateMessage(sender, "help: unknown command '" + help_cmd_name + "'."); break;
- }
- }
- else SendPrivateMessage(sender, "help . Available commands: exit, reco, script, send, connect.");
- break;
- default:
- SendPrivateMessage(sender, "Unknown command '" + cmd_name + "'. Use 'help' for help.");
- break;
- }
- }
- }
- }
- }
-}
diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs
new file mode 100644
index 00000000..78973a33
--- /dev/null
+++ b/MinecraftClient/ChatBot.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient
+{
+ ///
+ /// Welcome to the Bot API file !
+ /// The virtual class "ChatBot" contains anything you need for creating chat bots
+ /// Inherit from this class while adding your bot class to the folder "Bots".
+ /// Once your bot is created, read the explanations below to start using it in the MinecraftClient app.
+ ///
+ /// Pieces of code to add in other parts of the program for your bot. Line numbers are approximative.
+ /// McTcpClient:110 | if (Settings.YourBot_Enabled) { handler.BotLoad(new ChatBots.YourBot()); }
+ /// Settings.cs:73 | public static bool YourBot_Enabled = false;
+ /// Settings.cs:74 | private enum ParseMode { /* [...] */, YourBot };
+ /// Settings.cs:106 | case "yourbot": pMode = ParseMode.YourBot; break;
+ /// Settings.cs:197 | case ParseMode.YourBot: switch (argName.ToLower()) { case "enabled": YourBot_Enabled = str2bool(argValue); break; } break;
+ /// Settings.cs:267 | + "[YourBot]\r\n" + "enabled=false\r\n"
+ /// Here your are. Now you will have a setting in MinecraftClient.ini for enabling your brand new bot.
+ /// Delete MinecraftClient.ini to re-generate it or add the lines [YourBot] and enabled=true to the existing one.
+ ///
+
+ ///
+ /// The virtual class containing anything you need for creating chat bots.
+ ///
+
+ public abstract class ChatBot
+ {
+ public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost };
+
+ //Will be automatically set on bot loading, don't worry about this
+ public void SetHandler(McTcpClient handler) { this.handler = handler; }
+ private McTcpClient handler;
+
+ /* ================================================== */
+ /* Main methods to override for creating your bot */
+ /* ================================================== */
+
+ ///
+ /// Anything you want to initialize your bot, will be called on load by MinecraftCom
+ ///
+
+ public virtual void Initialize() { }
+
+ ///
+ /// Will be called every ~100ms (10fps) if loaded in MinecraftCom
+ ///
+
+ public virtual void Update() { }
+
+ ///
+ /// Any text sent by the server will be sent here by MinecraftCom
+ ///
+ /// Text from the server
+
+ public virtual void GetText(string text) { }
+
+ ///
+ /// Is called when the client has been disconnected fom the server
+ ///
+ /// Disconnect Reason
+ /// Kick message, if any
+ /// Return TRUE if the client is about to restart
+
+ public virtual bool OnDisconnect(DisconnectReason reason, string message) { return false; }
+
+ /* =================================================================== */
+ /* ToolBox - Methods below might be useful while creating your bot. */
+ /* You should not need to interact with other classes of the program. */
+ /* =================================================================== */
+
+ ///
+ /// Send text to the server. Can be anything such as chat messages or commands
+ ///
+ /// Text to send to the server
+
+ protected void SendText(string text)
+ {
+ ConsoleIO.WriteLineFormatted("§8BOT:" + text, false);
+ handler.SendChatMessage(text);
+ }
+
+ ///
+ /// Remove color codes ("§c") from a text message received from the server
+ ///
+
+ protected static string getVerbatim(string text)
+ {
+ if ( String.IsNullOrEmpty(text) )
+ return String.Empty;
+
+ int idx = 0;
+ var data = new char[text.Length];
+
+ for ( int i = 0; i < text.Length; i++ )
+ if ( text[i] != '§' )
+ data[idx++] = text[i];
+ else
+ i++;
+
+ return new string(data, 0, idx);
+ }
+
+ ///
+ /// Verify that a string contains only a-z A-Z 0-9 and _ characters.
+ ///
+
+ protected static bool isValidName(string username)
+ {
+ if ( String.IsNullOrEmpty(username) )
+ return false;
+
+ foreach ( char c in username )
+ if ( !((c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || c == '_') )
+ return false;
+
+ return true;
+ }
+
+ ///
+ /// Returns true is the text passed is a private message sent to the bot
+ ///
+ /// text to test
+ /// if it's a private message, this will contain the message
+ /// if it's a private message, this will contain the player name that sends the message
+ /// Returns true if the text is a private message
+
+ protected static bool isPrivateMessage(string text, ref string message, ref string sender)
+ {
+ if (text == "") { return false; }
+ string[] tmp = text.Split(' ');
+
+ try
+ {
+ //Detect vanilla /tell messages
+ //Someone whispers to you: message
+ if (tmp.Length > 2 && tmp[1] == "whispers")
+ {
+ message = text.Substring(tmp[0].Length + 18);
+ sender = tmp[0];
+ return isValidName(sender);
+ }
+
+ //Detect Essentials (Bukkit) /m messages
+ //[Someone -> me] message
+ else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "->"
+ && (tmp[2] == "me]" || tmp[2] == "moi]")) //'me' is replaced by 'moi' in french servers
+ {
+ message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 1);
+ sender = tmp[0].Substring(1);
+ if (sender[0] == '~') { sender = sender.Substring(1); }
+ return isValidName(sender);
+ }
+ else return false;
+ }
+ catch (IndexOutOfRangeException) { return false; }
+ }
+
+ ///
+ /// Returns true is the text passed is a public message written by a player on the chat
+ ///
+ /// text to test
+ /// if it's message, this will contain the message
+ /// if it's message, this will contain the player name that sends the message
+ /// Returns true if the text is a chat message
+
+ protected static bool isChatMessage(string text, ref string message, ref string sender)
+ {
+ //Detect chat messages
+ // message
+ //<*Faction Someone> message
+ //<*Faction Someone>: message
+ //<*Faction ~Nicknamed>: message
+ if (text == "") { return false; }
+ if (text[0] == '<')
+ {
+ try
+ {
+ text = text.Substring(1);
+ string[] tmp = text.Split('>');
+ sender = tmp[0];
+ message = text.Substring(sender.Length + 2);
+ if (message.Length > 1 && message[0] == ' ')
+ { message = message.Substring(1); }
+ tmp = sender.Split(' ');
+ sender = tmp[tmp.Length - 1];
+ if (sender[0] == '~') { sender = sender.Substring(1); }
+ return isValidName(sender);
+ }
+ catch (IndexOutOfRangeException) { return false; }
+ }
+ else return false;
+ }
+
+ ///
+ /// Writes some text in the console. Nothing will be sent to the server.
+ ///
+ /// Log text to write
+
+ public static void LogToConsole(string text)
+ {
+ ConsoleIO.WriteLineFormatted("§8[BOT] " + text, true);
+ }
+
+ ///
+ /// Disconnect from the server and restart the program
+ /// It will unload & reload all the bots and then reconnect to the server
+ ///
+
+ protected void ReconnectToTheServer() { ReconnectToTheServer(3); }
+
+ ///
+ /// Disconnect from the server and restart the program
+ /// It will unload & reload all the bots and then reconnect to the server
+ ///
+ /// If connection fails, the client will make X extra attempts
+
+ protected void ReconnectToTheServer(int ExtraAttempts)
+ {
+ McTcpClient.AttemptsLeft = ExtraAttempts;
+ Program.Restart();
+ }
+
+ ///
+ /// Disconnect from the server and exit the program
+ ///
+
+ protected void DisconnectAndExit()
+ {
+ Program.Exit();
+ }
+
+ ///
+ /// Unload the chatbot, and release associated memory.
+ ///
+
+ protected void UnloadBot()
+ {
+ handler.BotUnLoad(this);
+ }
+
+ ///
+ /// Send a private message to a player
+ ///
+ /// Player name
+ /// Message
+
+ protected void SendPrivateMessage(string player, string message)
+ {
+ SendText("/tell " + player + ' ' + message);
+ }
+
+ ///
+ /// Run a script from a file using a Scripting bot
+ ///
+ /// File name
+ /// Player name to send error messages, if applicable
+
+ protected void RunScript(string filename, string playername = "")
+ {
+ handler.BotLoad(new ChatBots.Script(filename, playername));
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/Alerts.cs b/MinecraftClient/ChatBots/Alerts.cs
new file mode 100644
index 00000000..a5b0794b
--- /dev/null
+++ b/MinecraftClient/ChatBots/Alerts.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// This bot make the console beep on some specified words. Useful to detect when someone is talking to you, for example.
+ ///
+
+ public class Alerts : ChatBot
+ {
+ private string[] dictionary = new string[0];
+ private string[] excludelist = new string[0];
+
+ public override void Initialize()
+ {
+ if (System.IO.File.Exists(Settings.Alerts_MatchesFile))
+ {
+ dictionary = System.IO.File.ReadAllLines(Settings.Alerts_MatchesFile);
+
+ for (int i = 0; i < dictionary.Length; i++)
+ {
+ dictionary[i] = dictionary[i].ToLower();
+ }
+ }
+ else LogToConsole("File not found: " + Settings.Alerts_MatchesFile);
+
+ if (System.IO.File.Exists(Settings.Alerts_ExcludesFile))
+ {
+ excludelist = System.IO.File.ReadAllLines(Settings.Alerts_ExcludesFile);
+
+ for (int i = 0; i < excludelist.Length; i++)
+ {
+ excludelist[i] = excludelist[i].ToLower();
+ }
+ }
+ else LogToConsole("File not found : " + Settings.Alerts_ExcludesFile);
+ }
+
+ public override void GetText(string text)
+ {
+ text = getVerbatim(text);
+ string comp = text.ToLower();
+ foreach (string alert in dictionary)
+ {
+ if (comp.Contains(alert))
+ {
+ bool ok = true;
+
+ foreach (string exclusion in excludelist)
+ {
+ if (comp.Contains(exclusion))
+ {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok)
+ {
+ if (Settings.Alerts_Beep_Enabled) { Console.Beep(); } //Text found !
+
+ if (ConsoleIO.basicIO) { ConsoleIO.WriteLine(comp.Replace(alert, "§c" + alert + "§r")); }
+ else
+ {
+
+ #region Displaying the text with the interesting part highlighted
+
+ Console.BackgroundColor = ConsoleColor.DarkGray;
+ Console.ForegroundColor = ConsoleColor.White;
+
+ //Will be used for text displaying
+ string[] temp = comp.Split(alert.Split(','), StringSplitOptions.RemoveEmptyEntries);
+ int p = 0;
+
+ //Special case : alert in the beginning of the text
+ string test = "";
+ for (int i = 0; i < alert.Length; i++)
+ {
+ test += comp[i];
+ }
+ if (test == alert)
+ {
+ Console.BackgroundColor = ConsoleColor.Yellow;
+ Console.ForegroundColor = ConsoleColor.Red;
+ for (int i = 0; i < alert.Length; i++)
+ {
+ ConsoleIO.Write(text[p]);
+ p++;
+ }
+ }
+
+ //Displaying the rest of the text
+ for (int i = 0; i < temp.Length; i++)
+ {
+ Console.BackgroundColor = ConsoleColor.DarkGray;
+ Console.ForegroundColor = ConsoleColor.White;
+ for (int j = 0; j < temp[i].Length; j++)
+ {
+ ConsoleIO.Write(text[p]);
+ p++;
+ }
+ Console.BackgroundColor = ConsoleColor.Yellow;
+ Console.ForegroundColor = ConsoleColor.Red;
+ try
+ {
+ for (int j = 0; j < alert.Length; j++)
+ {
+ ConsoleIO.Write(text[p]);
+ p++;
+ }
+ }
+ catch (IndexOutOfRangeException) { }
+ }
+ Console.BackgroundColor = ConsoleColor.Black;
+ Console.ForegroundColor = ConsoleColor.Gray;
+ ConsoleIO.Write('\n');
+
+ #endregion
+
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/AntiAFK.cs b/MinecraftClient/ChatBots/AntiAFK.cs
new file mode 100644
index 00000000..a54e8bcc
--- /dev/null
+++ b/MinecraftClient/ChatBots/AntiAFK.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// This bot sends a command every 60 seconds in order to stay non-afk.
+ ///
+
+ public class AntiAFK : ChatBot
+ {
+ private int count;
+ private int timeping;
+
+ ///
+ /// This bot sends a /ping command every X seconds in order to stay non-afk.
+ ///
+ /// Time amount between each ping (10 = 1s, 600 = 1 minute, etc.)
+
+ public AntiAFK(int pingparam)
+ {
+ count = 0;
+ timeping = pingparam;
+ if (timeping < 10) { timeping = 10; } //To avoid flooding
+ }
+
+ public override void Update()
+ {
+ count++;
+ if (count == timeping)
+ {
+ SendText(Settings.AntiAFK_Command);
+ count = 0;
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/AutoRelog.cs b/MinecraftClient/ChatBots/AutoRelog.cs
new file mode 100644
index 00000000..5f2b9e2c
--- /dev/null
+++ b/MinecraftClient/ChatBots/AutoRelog.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// This bot automatically re-join the server if kick message contains predefined string (Server is restarting ...)
+ ///
+
+ public class AutoRelog : ChatBot
+ {
+ private string[] dictionary = new string[0];
+ private int attempts;
+ private int delay;
+
+ ///
+ /// This bot automatically re-join the server if kick message contains predefined string
+ ///
+ /// Delay before re-joining the server (in seconds)
+ /// Number of retries if connection fails (-1 = infinite)
+
+ public AutoRelog(int DelayBeforeRelog, int retries)
+ {
+ attempts = retries;
+ if (attempts == -1) { attempts = int.MaxValue; }
+ McTcpClient.AttemptsLeft = attempts;
+ delay = DelayBeforeRelog;
+ if (delay < 1) { delay = 1; }
+ }
+
+ public override void Initialize()
+ {
+ McTcpClient.AttemptsLeft = attempts;
+ if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile))
+ {
+ dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile);
+
+ for (int i = 0; i < dictionary.Length; i++)
+ {
+ dictionary[i] = dictionary[i].ToLower();
+ }
+ }
+ else LogToConsole("File not found: " + Settings.AutoRelog_KickMessagesFile);
+ }
+
+ public override bool OnDisconnect(DisconnectReason reason, string message)
+ {
+ message = getVerbatim(message);
+ string comp = message.ToLower();
+ foreach (string msg in dictionary)
+ {
+ if (comp.Contains(msg))
+ {
+ LogToConsole("Waiting " + delay + " seconds before reconnecting...");
+ System.Threading.Thread.Sleep(delay * 1000);
+ McTcpClient.AttemptsLeft = attempts;
+ ReconnectToTheServer();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/ChatLog.cs b/MinecraftClient/ChatBots/ChatLog.cs
new file mode 100644
index 00000000..84ccb356
--- /dev/null
+++ b/MinecraftClient/ChatBots/ChatLog.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// This bot saves the received messages in a text file.
+ ///
+
+ public class ChatLog : ChatBot
+ {
+ public enum MessageFilter { AllText, AllMessages, OnlyChat, OnlyWhispers };
+ private bool dateandtime;
+ private bool saveOther = true;
+ private bool saveChat = true;
+ private bool savePrivate = true;
+ private string logfile;
+
+ ///
+ /// This bot saves the messages received in the specified file, with some filters and date/time tagging.
+ ///
+ /// The file to save the log in
+ /// The kind of messages to save
+ /// Add a date and time before each message
+
+ public ChatLog(string file, MessageFilter filter, bool AddDateAndTime)
+ {
+ dateandtime = AddDateAndTime;
+ logfile = file;
+ switch (filter)
+ {
+ case MessageFilter.AllText:
+ saveOther = true;
+ savePrivate = true;
+ saveChat = true;
+ break;
+ case MessageFilter.AllMessages:
+ saveOther = false;
+ savePrivate = true;
+ saveChat = true;
+ break;
+ case MessageFilter.OnlyChat:
+ saveOther = false;
+ savePrivate = false;
+ saveChat = true;
+ break;
+ case MessageFilter.OnlyWhispers:
+ saveOther = false;
+ savePrivate = true;
+ saveChat = false;
+ break;
+ }
+ }
+
+ public static MessageFilter str2filter(string filtername)
+ {
+ switch (filtername.ToLower())
+ {
+ case "all": return MessageFilter.AllText;
+ case "messages": return MessageFilter.AllMessages;
+ case "chat": return MessageFilter.OnlyChat;
+ case "private": return MessageFilter.OnlyWhispers;
+ default: return MessageFilter.AllText;
+ }
+ }
+
+ public override void GetText(string text)
+ {
+ text = getVerbatim(text);
+ string sender = "";
+ string message = "";
+
+ if (saveChat && isChatMessage(text, ref message, ref sender))
+ {
+ save("Chat " + sender + ": " + message);
+ }
+ else if (savePrivate && isPrivateMessage(text, ref message, ref sender))
+ {
+ save("Private " + sender + ": " + message);
+ }
+ else if (saveOther)
+ {
+ save("Other: " + text);
+ }
+ }
+
+ private void save(string tosave)
+ {
+ if (dateandtime)
+ {
+ int day = DateTime.Now.Day, month = DateTime.Now.Month;
+ int hour = DateTime.Now.Hour, minute = DateTime.Now.Minute, second = DateTime.Now.Second;
+
+ string D = day < 10 ? "0" + day : "" + day;
+ string M = month < 10 ? "0" + month : "" + day;
+ string Y = "" + DateTime.Now.Year;
+
+ string h = hour < 10 ? "0" + hour : "" + hour;
+ string m = minute < 10 ? "0" + minute : "" + minute;
+ string s = second < 10 ? "0" + second : "" + second;
+
+ tosave = "" + D + '-' + M + '-' + Y + ' ' + h + ':' + m + ':' + s + ' ' + tosave;
+ }
+
+ System.IO.FileStream stream = new System.IO.FileStream(logfile, System.IO.FileMode.OpenOrCreate);
+ System.IO.StreamWriter writer = new System.IO.StreamWriter(stream);
+ stream.Seek(0, System.IO.SeekOrigin.End);
+ writer.WriteLine(tosave);
+ writer.Dispose();
+ stream.Close();
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/HangmanGame.cs b/MinecraftClient/ChatBots/HangmanGame.cs
new file mode 100644
index 00000000..9b11cc2c
--- /dev/null
+++ b/MinecraftClient/ChatBots/HangmanGame.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// In-Chat Hangman game
+ ///
+
+ public class HangmanGame : ChatBot
+ {
+ private int vie = 0;
+ private int vie_param = 10;
+ private int compteur = 0;
+ private int compteur_param = 3000; //5 minutes
+ private bool running = false;
+ private bool[] discovered;
+ private string word = "";
+ private string letters = "";
+ private bool English;
+
+ ///
+ /// Le jeu du Pendu / Hangman Game
+ ///
+ /// if true, the game will be in english. If false, the game will be in french.
+
+ public HangmanGame(bool english)
+ {
+ English = english;
+ }
+
+ public override void Update()
+ {
+ if (running)
+ {
+ if (compteur > 0)
+ {
+ compteur--;
+ }
+ else
+ {
+ SendText(English ? "You took too long to try a letter." : "Temps imparti écoulé !");
+ SendText(English ? "Game canceled." : "Partie annulée.");
+ running = false;
+ }
+ }
+ }
+
+ public override void GetText(string text)
+ {
+ string message = "";
+ string username = "";
+ text = getVerbatim(text);
+
+ if (isPrivateMessage(text, ref message, ref username))
+ {
+ if (Settings.Bots_Owners.Contains(username.ToLower()))
+ {
+ switch (message)
+ {
+ case "start":
+ start();
+ break;
+ case "stop":
+ running = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (running && isChatMessage(text, ref message, ref username))
+ {
+ if (message.Length == 1)
+ {
+ char letter = message.ToUpper()[0];
+ if (letter >= 'A' && letter <= 'Z')
+ {
+ if (letters.Contains(letter))
+ {
+ SendText(English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé."));
+ }
+ else
+ {
+ letters += letter;
+ compteur = compteur_param;
+
+ if (word.Contains(letter))
+ {
+ for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { discovered[i] = true; } }
+ SendText(English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)"));
+ }
+ else
+ {
+ vie--;
+ if (vie == 0)
+ {
+ SendText(English ? "Game Over! :]" : "Perdu ! Partie terminée :]");
+ SendText(English ? ("The word was: " + word) : ("Le mot était : " + word));
+ running = false;
+ }
+ else SendText(English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non."));
+ }
+
+ if (running)
+ {
+ SendText(English ? ("Mysterious word: " + word_cached + " (lives : " + vie + ")")
+ : ("Mot mystère : " + word_cached + " (vie : " + vie + ")"));
+ }
+
+ if (winner)
+ {
+ SendText(English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !"));
+ running = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void start()
+ {
+ vie = vie_param;
+ running = true;
+ letters = "";
+ word = chooseword();
+ compteur = compteur_param;
+ discovered = new bool[word.Length];
+
+ SendText(English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio");
+ SendText(English ? ("Mysterious word: " + word_cached + " (lives : " + vie + ")")
+ : ("Mot mystère : " + word_cached + " (vie : " + vie + ")"));
+ SendText(English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)"));
+ }
+
+ private string chooseword()
+ {
+ if (System.IO.File.Exists(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR))
+ {
+ string[] dico = System.IO.File.ReadAllLines(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR);
+ return dico[new Random().Next(dico.Length)];
+ }
+ else
+ {
+ LogToConsole(English ? "File not found: " + Settings.Hangman_FileWords_EN : "Fichier introuvable : " + Settings.Hangman_FileWords_FR);
+ return English ? "WORDSAREMISSING" : "DICOMANQUANT";
+ }
+ }
+
+ private string word_cached
+ {
+ get
+ {
+ string printed = "";
+ for (int i = 0; i < word.Length; i++)
+ {
+ if (discovered[i])
+ {
+ printed += word[i];
+ }
+ else printed += '_';
+ }
+ return printed;
+ }
+ }
+
+ private bool winner
+ {
+ get
+ {
+ for (int i = 0; i < discovered.Length; i++)
+ {
+ if (!discovered[i])
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/PlayerListLogger.cs b/MinecraftClient/ChatBots/PlayerListLogger.cs
new file mode 100644
index 00000000..9124b664
--- /dev/null
+++ b/MinecraftClient/ChatBots/PlayerListLogger.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// This bot sends a /list command every X seconds and save the result.
+ ///
+
+ public class PlayerListLogger : ChatBot
+ {
+ private int count;
+ private int timeping;
+ private string file;
+
+ ///
+ /// This bot sends a /list command every X seconds and save the result.
+ ///
+ /// Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.)
+
+ public PlayerListLogger(int pingparam, string filetosavein)
+ {
+ count = 0;
+ file = filetosavein;
+ timeping = pingparam;
+ if (timeping < 10) { timeping = 10; } //To avoid flooding
+
+ }
+
+ public override void Update()
+ {
+ count++;
+ if (count == timeping)
+ {
+ SendText("/list");
+ count = 0;
+ }
+ }
+
+ public override void GetText(string text)
+ {
+ if (text.Contains("Joueurs en ligne") || text.Contains("Connected:") || text.Contains("online:"))
+ {
+ LogToConsole("Saving Player List");
+ DateTime now = DateTime.Now;
+ string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']';
+ System.IO.File.AppendAllText(file, TimeStamp + "\n" + getVerbatim(text) + "\n\n");
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/RemoteControl.cs b/MinecraftClient/ChatBots/RemoteControl.cs
new file mode 100644
index 00000000..df5cde1b
--- /dev/null
+++ b/MinecraftClient/ChatBots/RemoteControl.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// Allow to perform operations using whispers to the bot
+ ///
+
+ public class RemoteControl : ChatBot
+ {
+ public override void GetText(string text)
+ {
+ text = getVerbatim(text);
+ string command = "", sender = "";
+ if (isPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower()))
+ {
+ string cmd_name = command.Split(' ')[0];
+ switch (cmd_name.ToLower())
+ {
+ case "exit":
+ DisconnectAndExit();
+ break;
+ case "reco":
+ ReconnectToTheServer();
+ break;
+ case "script":
+ if (command.Length >= 8)
+ RunScript(command.Substring(7), sender);
+ break;
+ case "send":
+ if (command.Length >= 6)
+ SendText(command.Substring(5));
+ break;
+ case "connect":
+ if (command.Length >= 9)
+ {
+ Settings.ServerIP = command.Substring(8);
+ ReconnectToTheServer();
+ }
+ break;
+ case "help":
+ if (command.Length >= 6)
+ {
+ string help_cmd_name = command.Substring(5).ToLower();
+ switch (help_cmd_name)
+ {
+ case "exit": SendPrivateMessage(sender, "exit: disconnect from the server."); break;
+ case "reco": SendPrivateMessage(sender, "reco: restart and reconnct to the server."); break;
+ case "script": SendPrivateMessage(sender, "script : run a script file."); break;
+ case "send": SendPrivateMessage(sender, "send : send a chat message or command."); break;
+ case "connect": SendPrivateMessage(sender, "connect : connect to the specified server."); break;
+ case "help": SendPrivateMessage(sender, "help : show brief help about a command."); break;
+ default: SendPrivateMessage(sender, "help: unknown command '" + help_cmd_name + "'."); break;
+ }
+ }
+ else SendPrivateMessage(sender, "help . Available commands: exit, reco, script, send, connect.");
+ break;
+ default:
+ SendPrivateMessage(sender, "Unknown command '" + cmd_name + "'. Use 'help' for help.");
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs
new file mode 100644
index 00000000..3517a827
--- /dev/null
+++ b/MinecraftClient/ChatBots/Script.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// Runs a list of commands
+ ///
+
+ public class Script : ChatBot
+ {
+ private string file;
+ private string[] lines = new string[0];
+ private int sleepticks = 10;
+ private int sleepticks_interval = 10;
+ private int nextline = 0;
+ private string owner;
+
+ public Script(string filename)
+ {
+ file = filename;
+ }
+
+ public Script(string filename, string ownername)
+ : this(filename)
+ {
+ if (ownername != "")
+ owner = ownername;
+ }
+
+ public static bool lookForScript(ref string filename)
+ {
+ //Automatically look in subfolders and try to add ".txt" file extension
+ string[] files = new string[]
+ {
+ filename,
+ filename + ".txt",
+ "scripts\\" + filename,
+ "scripts\\" + filename + ".txt",
+ "config\\" + filename,
+ "config\\" + filename + ".txt",
+ };
+
+ foreach (string possible_file in files)
+ {
+ if (System.IO.File.Exists(possible_file))
+ {
+ filename = possible_file;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public override void Initialize()
+ {
+ //Load the given file from the startup parameters
+ if (lookForScript(ref file))
+ {
+ lines = System.IO.File.ReadAllLines(file);
+ if (owner != null) { SendPrivateMessage(owner, "Script '" + file + "' loaded."); }
+ }
+ else
+ {
+ LogToConsole("File not found: '" + file + "'");
+ if (owner != null)
+ SendPrivateMessage(owner, "File not found: '" + file + "'");
+ UnloadBot(); //No need to keep the bot active
+ }
+ }
+
+ public override void Update()
+ {
+ if (sleepticks > 0) { sleepticks--; }
+ else
+ {
+ if (nextline < lines.Length) //Is there an instruction left to interpret?
+ {
+ string instruction_line = lines[nextline].Trim(); // Removes all whitespaces at start and end of current line
+ nextline++; //Move the cursor so that the next time the following line will be interpreted
+ sleepticks = sleepticks_interval; //Used to delay next command sending and prevent from beign kicked for spamming
+
+ if (instruction_line.Length > 1)
+ {
+ if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
+ {
+ string instruction_name = instruction_line.Split(' ')[0];
+ switch (instruction_name.ToLower())
+ {
+ case "send":
+ SendText(instruction_line.Substring(5, instruction_line.Length - 5));
+ break;
+ case "wait":
+ int ticks = 10;
+ try
+ {
+ ticks = Convert.ToInt32(instruction_line.Substring(5, instruction_line.Length - 5));
+ }
+ catch { }
+ sleepticks = ticks;
+ break;
+ case "disconnect":
+ DisconnectAndExit();
+ break;
+ case "exit": //Exit bot & stay connected to the server
+ UnloadBot();
+ break;
+ case "connect":
+ if (instruction_line.Length >= 9)
+ {
+ Settings.ServerIP = instruction_line.Substring(8);
+ ReconnectToTheServer();
+ }
+ break;
+ default:
+ sleepticks = 0; Update(); //Unknown command : process next line immediately
+ break;
+ }
+ }
+ else { sleepticks = 0; Update(); } //Comment: process next line immediately
+ }
+ }
+ else
+ {
+ //No more instructions to interpret
+ UnloadBot();
+ }
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs
new file mode 100644
index 00000000..eee6f921
--- /dev/null
+++ b/MinecraftClient/ChatBots/ScriptScheduler.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Globalization;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// Trigger scripts on specific events
+ ///
+
+ public class ScriptScheduler : ChatBot
+ {
+ private class TaskDesc
+ {
+ public string script_file = null;
+ public bool triggerOnFirstLogin = false;
+ public bool triggerOnLogin = false;
+ public bool triggerOnTime = false;
+ public List triggerOnTime_Times = new List();
+ public bool alreadyTriggered = false;
+ }
+
+ private static bool firstlogin_done = false;
+
+ private string tasksfile;
+ private bool serverlogin_done;
+ private List tasks = new List();
+ private int verifytasks_timeleft = 10;
+ private int verifytasks_delay = 10;
+
+ public ScriptScheduler(string tasksfile)
+ {
+ this.tasksfile = tasksfile;
+ serverlogin_done = false;
+ }
+
+ public override void Initialize()
+ {
+ //Load the given file from the startup parameters
+ if (System.IO.File.Exists(tasksfile))
+ {
+ TaskDesc current_task = null;
+ String[] lines = System.IO.File.ReadAllLines(tasksfile);
+ foreach (string lineRAW in lines)
+ {
+ string line = lineRAW.Split('#')[0].Trim();
+ if (line.Length > 0)
+ {
+ if (line[0] == '[' && line[line.Length - 1] == ']')
+ {
+ switch (line.Substring(1, line.Length - 2).ToLower())
+ {
+ case "task":
+ checkAddTask(current_task);
+ current_task = new TaskDesc(); //Create a blank task
+ break;
+ }
+ }
+ else if (current_task != null)
+ {
+ string argName = line.Split('=')[0];
+ if (line.Length > (argName.Length + 1))
+ {
+ string argValue = line.Substring(argName.Length + 1);
+ switch (argName.ToLower())
+ {
+ case "triggeronfirstlogin": current_task.triggerOnFirstLogin = Settings.str2bool(argValue); break;
+ case "triggeronlogin": current_task.triggerOnLogin = Settings.str2bool(argValue); break;
+ case "triggerontime": current_task.triggerOnTime = Settings.str2bool(argValue); break;
+ case "timevalue": try { current_task.triggerOnTime_Times.Add(DateTime.ParseExact(argValue, "HH:mm", CultureInfo.InvariantCulture)); }
+ catch { } break;
+ case "script": current_task.script_file = argValue; break;
+ }
+ }
+ }
+ }
+ }
+ checkAddTask(current_task);
+ }
+ else
+ {
+ LogToConsole("File not found: '" + tasksfile + "'");
+ UnloadBot(); //No need to keep the bot active
+ }
+ }
+
+ private void checkAddTask(TaskDesc current_task)
+ {
+ if (current_task != null)
+ {
+ //Check if we built a valid task before adding it
+ if (current_task.script_file != null && Script.lookForScript(ref current_task.script_file) //Check if file exists
+ && (current_task.triggerOnLogin || (current_task.triggerOnTime && current_task.triggerOnTime_Times.Count > 0))) //Look for a valid trigger
+ {
+ tasks.Add(current_task);
+ }
+ }
+ }
+
+ public override void Update()
+ {
+ if (verifytasks_timeleft <= 0)
+ {
+ verifytasks_timeleft = verifytasks_delay;
+ if (serverlogin_done)
+ {
+ foreach (TaskDesc task in tasks)
+ {
+ if (task.triggerOnTime)
+ {
+ foreach (DateTime time in task.triggerOnTime_Times)
+ {
+ if (time.Hour == DateTime.Now.Hour && time.Minute == DateTime.Now.Minute)
+ {
+ if (!task.alreadyTriggered)
+ {
+ task.alreadyTriggered = true;
+ RunScript(task.script_file);
+ }
+ }
+ }
+ }
+ else task.alreadyTriggered = false;
+ }
+ }
+ else
+ {
+ foreach (TaskDesc task in tasks)
+ {
+ if (task.triggerOnLogin || (firstlogin_done == false && task.triggerOnFirstLogin))
+ RunScript(task.script_file);
+ }
+
+ firstlogin_done = true;
+ serverlogin_done = true;
+ }
+ }
+ else verifytasks_timeleft--;
+ }
+ }
+}
diff --git a/MinecraftClient/ChatBots/TestBot.cs b/MinecraftClient/ChatBots/TestBot.cs
new file mode 100644
index 00000000..ffd921f8
--- /dev/null
+++ b/MinecraftClient/ChatBots/TestBot.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.ChatBots
+{
+ ///
+ /// Example of message receiving.
+ ///
+
+ public class TestBot : ChatBot
+ {
+ public override void GetText(string text)
+ {
+ string message = "";
+ string username = "";
+ text = getVerbatim(text);
+
+ if (isPrivateMessage(text, ref message, ref username))
+ {
+ ConsoleIO.WriteLine("Bot: " + username + " told me : " + message);
+ }
+ else if (isChatMessage(text, ref message, ref username))
+ {
+ ConsoleIO.WriteLine("Bot: " + username + " said : " + message);
+ }
+ }
+ }
+}
diff --git a/MinecraftClient/ConsoleIO.cs b/MinecraftClient/ConsoleIO.cs
index 86059ebc..6f08abcb 100644
--- a/MinecraftClient/ConsoleIO.cs
+++ b/MinecraftClient/ConsoleIO.cs
@@ -25,7 +25,10 @@ namespace MinecraftClient
private static bool reading_lock = false;
private static bool writing_lock = false;
- #region Read User Input
+ ///
+ /// Read a password from the standard input
+ ///
+
public static string ReadPassword()
{
string password = "";
@@ -71,6 +74,10 @@ namespace MinecraftClient
return password;
}
+ ///
+ /// Read a line from the standard input
+ ///
+
public static string ReadLine()
{
if (basicIO) { return Console.ReadLine(); }
@@ -174,9 +181,11 @@ namespace MinecraftClient
previous.AddLast(buffer + buffer2);
return buffer + buffer2;
}
- #endregion
+
+ ///
+ /// Write a string to the standard output, without newline character
+ ///
- #region Console Output
public static void Write(string text)
{
if (basicIO) { Console.Write(text); return; }
@@ -216,16 +225,79 @@ namespace MinecraftClient
writing_lock = false;
}
+ ///
+ /// Write a string to the standard output with a trailing newline
+ ///
+
public static void WriteLine(string line)
{
Write(line + '\n');
}
+ ///
+ /// Write a single character to the standard output
+ ///
+
public static void Write(char c)
{
Write("" + c);
}
- #endregion
+
+ ///
+ /// Write a Minecraft-Formatted string to the standard output, using §c color codes
+ ///
+ /// String to write
+ /// If false, space are printed instead of newlines
+
+ public static void WriteLineFormatted(string str, bool acceptnewlines)
+ {
+ if (basicIO) { Console.WriteLine(str); return; }
+ if (!String.IsNullOrEmpty(str))
+ {
+ if (Settings.chatTimeStamps)
+ {
+ int hour = DateTime.Now.Hour, minute = DateTime.Now.Minute, second = DateTime.Now.Second;
+ ConsoleIO.Write(hour.ToString("00") + ':' + minute.ToString("00") + ':' + second.ToString("00") + ' ');
+ }
+ if (!acceptnewlines) { str = str.Replace('\n', ' '); }
+ if (ConsoleIO.basicIO) { ConsoleIO.WriteLine(str); return; }
+ string[] subs = str.Split(new char[] { '§' });
+ if (subs[0].Length > 0) { ConsoleIO.Write(subs[0]); }
+ for (int i = 1; i < subs.Length; i++)
+ {
+ if (subs[i].Length > 0)
+ {
+ switch (subs[i][0])
+ {
+ case '0': Console.ForegroundColor = ConsoleColor.Gray; break; //Should be Black but Black is non-readable on a black background
+ case '1': Console.ForegroundColor = ConsoleColor.DarkBlue; break;
+ case '2': Console.ForegroundColor = ConsoleColor.DarkGreen; break;
+ case '3': Console.ForegroundColor = ConsoleColor.DarkCyan; break;
+ case '4': Console.ForegroundColor = ConsoleColor.DarkRed; break;
+ case '5': Console.ForegroundColor = ConsoleColor.DarkMagenta; break;
+ case '6': Console.ForegroundColor = ConsoleColor.DarkYellow; break;
+ case '7': Console.ForegroundColor = ConsoleColor.Gray; break;
+ case '8': Console.ForegroundColor = ConsoleColor.DarkGray; break;
+ case '9': Console.ForegroundColor = ConsoleColor.Blue; break;
+ case 'a': Console.ForegroundColor = ConsoleColor.Green; break;
+ case 'b': Console.ForegroundColor = ConsoleColor.Cyan; break;
+ case 'c': Console.ForegroundColor = ConsoleColor.Red; break;
+ case 'd': Console.ForegroundColor = ConsoleColor.Magenta; break;
+ case 'e': Console.ForegroundColor = ConsoleColor.Yellow; break;
+ case 'f': Console.ForegroundColor = ConsoleColor.White; break;
+ case 'r': Console.ForegroundColor = ConsoleColor.White; break;
+ }
+
+ if (subs[i].Length > 1)
+ {
+ ConsoleIO.Write(subs[i].Substring(1, subs[i].Length - 1));
+ }
+ }
+ }
+ ConsoleIO.Write('\n');
+ }
+ Console.ForegroundColor = ConsoleColor.Gray;
+ }
#region Subfunctions
private static void ClearLineAndBuffer()
diff --git a/MinecraftClient/Crypto.cs b/MinecraftClient/Crypto/CryptoHandler.cs
similarity index 50%
rename from MinecraftClient/Crypto.cs
rename to MinecraftClient/Crypto/CryptoHandler.cs
index 80501a98..7d31e162 100644
--- a/MinecraftClient/Crypto.cs
+++ b/MinecraftClient/Crypto/CryptoHandler.cs
@@ -3,14 +3,15 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
+using System.IO;
-namespace MinecraftClient
+namespace MinecraftClient.Crypto
{
///
/// Methods for handling all the crypto stuff: RSA (Encryption Key Request), AES (Encrypted Stream), SHA-1 (Server Hash).
///
- public class Crypto
+ public class CryptoHandler
{
///
/// Get a cryptographic service for encrypting data using the server's RSA public key
@@ -192,249 +193,20 @@ namespace MinecraftClient
}
///
- /// Interface for AES stream
- /// Allows to use any object which has a Read() and Write() method.
+ /// Get a new AES-encrypted stream for wrapping a non-encrypted stream.
///
+ /// Stream to encrypt
+ /// Key to use
+ /// Padding provider for Mono implementation
+ /// Return an appropriate stream depending on the framework being used
- public interface IAesStream
+ public static IAesStream getAesStream(Stream underlyingStream, byte[] AesKey, IPaddingProvider paddingProvider)
{
- int Read(byte[] buffer, int offset, int count);
- void Write(byte[] buffer, int offset, int count);
- }
-
- ///
- /// An encrypted stream using AES, used for encrypting network data on the fly using AES.
- /// This is the regular AesStream class used with the regular .NET framework from Microsoft.
- ///
-
- public class AesStream : System.IO.Stream, IAesStream
- {
- CryptoStream enc;
- CryptoStream dec;
- public AesStream(System.IO.Stream stream, byte[] key)
+ if (Program.isUsingMono)
{
- BaseStream = stream;
- enc = new CryptoStream(stream, GenerateAES(key).CreateEncryptor(), CryptoStreamMode.Write);
- dec = new CryptoStream(stream, GenerateAES(key).CreateDecryptor(), CryptoStreamMode.Read);
- }
- public System.IO.Stream BaseStream { get; set; }
-
- public override bool CanRead
- {
- get { return true; }
- }
-
- public override bool CanSeek
- {
- get { return false; }
- }
-
- public override bool CanWrite
- {
- get { return true; }
- }
-
- public override void Flush()
- {
- BaseStream.Flush();
- }
-
- public override long Length
- {
- get { throw new NotSupportedException(); }
- }
-
- public override long Position
- {
- get
- {
- throw new NotSupportedException();
- }
- set
- {
- throw new NotSupportedException();
- }
- }
-
- public override int ReadByte()
- {
- return dec.ReadByte();
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return dec.Read(buffer, offset, count);
- }
-
- public override long Seek(long offset, System.IO.SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
-
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- public override void WriteByte(byte b)
- {
- enc.WriteByte(b);
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- enc.Write(buffer, offset, count);
- }
-
- private RijndaelManaged GenerateAES(byte[] key)
- {
- RijndaelManaged cipher = new RijndaelManaged();
- cipher.Mode = CipherMode.CFB;
- cipher.Padding = PaddingMode.None;
- cipher.KeySize = 128;
- cipher.FeedbackSize = 8;
- cipher.Key = key;
- cipher.IV = key;
- return cipher;
- }
- }
-
- ///
- /// An encrypted stream using AES, used for encrypting network data on the fly using AES.
- /// This is a mono-compatible adaptation which only sends and receive 16 bytes at a time, and manually transforms blocks.
- /// Data is cached before reaching the 128bits block size necessary for mono which is not CFB-8 compatible.
- ///
-
- public class MonoAesStream : System.IO.Stream, IAesStream
- {
- ICryptoTransform enc;
- ICryptoTransform dec;
- List dec_cache = new List();
- List tosend_cache = new List();
- public MonoAesStream(System.IO.Stream stream, byte[] key)
- {
- BaseStream = stream;
- RijndaelManaged aes = GenerateAES(key);
- enc = aes.CreateEncryptor();
- dec = aes.CreateDecryptor();
- }
- public System.IO.Stream BaseStream { get; set; }
-
- public override bool CanRead
- {
- get { return true; }
- }
-
- public override bool CanSeek
- {
- get { return false; }
- }
-
- public override bool CanWrite
- {
- get { return true; }
- }
-
- public override void Flush()
- {
- BaseStream.Flush();
- }
-
- public override long Length
- {
- get { throw new NotSupportedException(); }
- }
-
- public override long Position
- {
- get
- {
- throw new NotSupportedException();
- }
- set
- {
- throw new NotSupportedException();
- }
- }
-
- public override int ReadByte()
- {
- byte[] temp = new byte[1];
- Read(temp, 0, 1);
- return temp[0];
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- while (dec_cache.Count < count)
- {
- byte[] temp_in = new byte[16];
- byte[] temp_out = new byte[16];
- int read = 0;
- while (read < 16)
- read += BaseStream.Read(temp_in, read, 16 - read);
- dec.TransformBlock(temp_in, 0, 16, temp_out, 0);
- foreach (byte b in temp_out)
- dec_cache.Add(b);
- }
-
- for (int i = offset; i - offset < count; i++)
- {
- buffer[i] = dec_cache[0];
- dec_cache.RemoveAt(0);
- }
-
- return count;
- }
-
- public override long Seek(long offset, System.IO.SeekOrigin origin)
- {
- throw new NotSupportedException();
- }
-
- public override void SetLength(long value)
- {
- throw new NotSupportedException();
- }
-
- public override void WriteByte(byte b)
- {
- Write(new byte[] { b }, 0, 1);
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- for (int i = offset; i - offset < count; i++)
- tosend_cache.Add(buffer[i]);
-
- if (tosend_cache.Count < 16)
- tosend_cache.AddRange(MinecraftCom.getPaddingPacket());
-
- while (tosend_cache.Count > 16)
- {
- byte[] temp_in = new byte[16];
- byte[] temp_out = new byte[16];
- for (int i = 0; i < 16; i++)
- {
- temp_in[i] = tosend_cache[0];
- tosend_cache.RemoveAt(0);
- }
- enc.TransformBlock(temp_in, 0, 16, temp_out, 0);
- BaseStream.Write(temp_out, 0, 16);
- }
- }
-
- private RijndaelManaged GenerateAES(byte[] key)
- {
- RijndaelManaged cipher = new RijndaelManaged();
- cipher.Mode = CipherMode.CFB;
- cipher.Padding = PaddingMode.None;
- cipher.KeySize = 128;
- cipher.FeedbackSize = 8;
- cipher.Key = key;
- cipher.IV = key;
- return cipher;
+ return new Streams.MonoAesStream(underlyingStream, AesKey, paddingProvider);
}
+ else return new Streams.RegularAesStream(underlyingStream, AesKey);
}
}
}
diff --git a/MinecraftClient/Crypto/IAesStream.cs b/MinecraftClient/Crypto/IAesStream.cs
new file mode 100644
index 00000000..23f4a83b
--- /dev/null
+++ b/MinecraftClient/Crypto/IAesStream.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.Crypto
+{
+ ///
+ /// Interface for AES stream
+ /// Allows to use a different implementation depending on the framework being used.
+ ///
+
+ public interface IAesStream
+ {
+ int Read(byte[] buffer, int offset, int count);
+ void Write(byte[] buffer, int offset, int count);
+ }
+}
diff --git a/MinecraftClient/Crypto/IPaddingProvider.cs b/MinecraftClient/Crypto/IPaddingProvider.cs
new file mode 100644
index 00000000..48290494
--- /dev/null
+++ b/MinecraftClient/Crypto/IPaddingProvider.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MinecraftClient.Crypto
+{
+ ///
+ /// Interface for padding provider
+ /// Allow to get a padding plugin message from the current network protocol implementation.
+ ///
+
+ public interface IPaddingProvider
+ {
+ byte[] getPaddingPacket();
+ }
+}
diff --git a/MinecraftClient/Crypto/Streams/MonoAesStream.cs b/MinecraftClient/Crypto/Streams/MonoAesStream.cs
new file mode 100644
index 00000000..5382477e
--- /dev/null
+++ b/MinecraftClient/Crypto/Streams/MonoAesStream.cs
@@ -0,0 +1,148 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Security.Cryptography;
+using System.IO;
+
+namespace MinecraftClient.Crypto.Streams
+{
+ ///
+ /// An encrypted stream using AES, used for encrypting network data on the fly using AES.
+ /// This is a mono-compatible adaptation which only sends and receive 16 bytes at a time, and manually transforms blocks.
+ /// Data is cached before reaching the 128bits block size necessary for mono which is not CFB-8 compatible.
+ ///
+
+ public class MonoAesStream : Stream, IAesStream
+ {
+ IPaddingProvider pad;
+ ICryptoTransform enc;
+ ICryptoTransform dec;
+ List dec_cache = new List();
+ List tosend_cache = new List();
+ public MonoAesStream(System.IO.Stream stream, byte[] key, IPaddingProvider provider)
+ {
+ BaseStream = stream;
+ RijndaelManaged aes = GenerateAES(key);
+ enc = aes.CreateEncryptor();
+ dec = aes.CreateDecryptor();
+ }
+ public System.IO.Stream BaseStream { get; set; }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override void Flush()
+ {
+ BaseStream.Flush();
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ set
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override int ReadByte()
+ {
+ byte[] temp = new byte[1];
+ Read(temp, 0, 1);
+ return temp[0];
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ while (dec_cache.Count < count)
+ {
+ byte[] temp_in = new byte[16];
+ byte[] temp_out = new byte[16];
+ int read = 0;
+ while (read < 16)
+ read += BaseStream.Read(temp_in, read, 16 - read);
+ dec.TransformBlock(temp_in, 0, 16, temp_out, 0);
+ foreach (byte b in temp_out)
+ dec_cache.Add(b);
+ }
+
+ for (int i = offset; i - offset < count; i++)
+ {
+ buffer[i] = dec_cache[0];
+ dec_cache.RemoveAt(0);
+ }
+
+ return count;
+ }
+
+ public override long Seek(long offset, System.IO.SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void WriteByte(byte b)
+ {
+ Write(new byte[] { b }, 0, 1);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ for (int i = offset; i - offset < count; i++)
+ tosend_cache.Add(buffer[i]);
+
+ if (tosend_cache.Count < 16)
+ tosend_cache.AddRange(pad.getPaddingPacket());
+
+ while (tosend_cache.Count > 16)
+ {
+ byte[] temp_in = new byte[16];
+ byte[] temp_out = new byte[16];
+ for (int i = 0; i < 16; i++)
+ {
+ temp_in[i] = tosend_cache[0];
+ tosend_cache.RemoveAt(0);
+ }
+ enc.TransformBlock(temp_in, 0, 16, temp_out, 0);
+ BaseStream.Write(temp_out, 0, 16);
+ }
+ }
+
+ private RijndaelManaged GenerateAES(byte[] key)
+ {
+ RijndaelManaged cipher = new RijndaelManaged();
+ cipher.Mode = CipherMode.CFB;
+ cipher.Padding = PaddingMode.None;
+ cipher.KeySize = 128;
+ cipher.FeedbackSize = 8;
+ cipher.Key = key;
+ cipher.IV = key;
+ return cipher;
+ }
+ }
+}
diff --git a/MinecraftClient/Crypto/Streams/RegularAesStream.cs b/MinecraftClient/Crypto/Streams/RegularAesStream.cs
new file mode 100644
index 00000000..e3d021c0
--- /dev/null
+++ b/MinecraftClient/Crypto/Streams/RegularAesStream.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Security.Cryptography;
+using System.IO;
+
+namespace MinecraftClient.Crypto.Streams
+{
+ ///
+ /// An encrypted stream using AES, used for encrypting network data on the fly using AES.
+ /// This is the regular AesStream class used with the regular .NET framework from Microsoft.
+ ///
+
+ public class RegularAesStream : Stream, IAesStream
+ {
+ CryptoStream enc;
+ CryptoStream dec;
+ public RegularAesStream(Stream stream, byte[] key)
+ {
+ BaseStream = stream;
+ enc = new CryptoStream(stream, GenerateAES(key).CreateEncryptor(), CryptoStreamMode.Write);
+ dec = new CryptoStream(stream, GenerateAES(key).CreateDecryptor(), CryptoStreamMode.Read);
+ }
+ public System.IO.Stream BaseStream { get; set; }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override void Flush()
+ {
+ BaseStream.Flush();
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ throw new NotSupportedException();
+ }
+ set
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override int ReadByte()
+ {
+ return dec.ReadByte();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return dec.Read(buffer, offset, count);
+ }
+
+ public override long Seek(long offset, System.IO.SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void WriteByte(byte b)
+ {
+ enc.WriteByte(b);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ enc.Write(buffer, offset, count);
+ }
+
+ private RijndaelManaged GenerateAES(byte[] key)
+ {
+ RijndaelManaged cipher = new RijndaelManaged();
+ cipher.Mode = CipherMode.CFB;
+ cipher.Padding = PaddingMode.None;
+ cipher.KeySize = 128;
+ cipher.FeedbackSize = 8;
+ cipher.Key = key;
+ cipher.IV = key;
+ return cipher;
+ }
+ }
+}
diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs
index 6f1a3adf..ba48a006 100644
--- a/MinecraftClient/McTcpClient.cs
+++ b/MinecraftClient/McTcpClient.cs
@@ -6,50 +6,63 @@ using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Net;
+using MinecraftClient.Protocol;
+using MinecraftClient.Proxy;
namespace MinecraftClient
{
///
/// The main client class, used to connect to a Minecraft server.
- /// It allows message sending and text receiving.
///
- class McTcpClient
+ public class McTcpClient : IMinecraftComHandler
{
+ private List bots = new List();
+ private static List scripts_on_hold = new List();
+ public void BotLoad(ChatBot b) { b.SetHandler(this); bots.Add(b); b.Initialize(); Settings.SingleCommand = ""; }
+ public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); }
+ public void BotClear() { bots.Clear(); }
+
public static int AttemptsLeft = 0;
- string host;
- int port;
- string username;
- string text;
- Thread t_updater;
- Thread t_sender;
+ private string host;
+ private int port;
+ private string username;
+ private string uuid;
+ private string sessionid;
+
+ public int getServerPort() { return port; }
+ public string getServerHost() { return host; }
+ public string getUsername() { return username; }
+ public string getUserUUID() { return uuid; }
+ public string getSessionID() { return sessionid; }
+
TcpClient client;
- MinecraftCom handler;
+ IMinecraftCom handler;
///
- /// Starts the main chat client, wich will login to the server using the MinecraftCom class.
+ /// Starts the main chat client
///
/// The chosen username of a premium Minecraft Account
/// A valid sessionID obtained with MinecraftCom.GetLogin()
/// The server IP (serveradress or serveradress:port)
- public McTcpClient(string username, string uuid, string sessionID, string server_port, MinecraftCom handler)
+ public McTcpClient(string username, string uuid, string sessionID, int protocolversion, string server_port)
{
- StartClient(username, uuid, sessionID, server_port, false, handler, "");
+ StartClient(username, uuid, sessionID, server_port, protocolversion, false, "");
}
///
- /// Starts the main chat client in single command sending mode, wich will login to the server using the MinecraftCom class, send the command and close.
+ /// Starts the main chat client in single command sending mode
///
/// The chosen username of a premium Minecraft Account
/// A valid sessionID obtained with MinecraftCom.GetLogin()
/// The server IP (serveradress or serveradress:port)
/// The text or command to send.
- public McTcpClient(string username, string uuid, string sessionID, string server_port, MinecraftCom handler, string command)
+ public McTcpClient(string username, string uuid, string sessionID, string server_port, int protocolversion, string command)
{
- StartClient(username, uuid, sessionID, server_port, true, handler, command);
+ StartClient(username, uuid, sessionID, server_port, protocolversion, true, command);
}
///
@@ -61,12 +74,15 @@ namespace MinecraftClient
/// If set to true, the client will send a single command and then disconnect from the server
/// The text or command to send. Will only be sent if singlecommand is set to true.
- private void StartClient(string user, string uuid, string sessionID, string server_port, bool singlecommand, MinecraftCom handler, string command)
+ private void StartClient(string user, string uuid, string sessionID, string server_port, int protocolversion, bool singlecommand, string command)
{
- this.handler = handler;
- username = user;
string[] sip = server_port.Split(':');
- host = sip[0];
+
+ this.sessionid = sessionID;
+ this.uuid = uuid;
+ this.username = user;
+ this.host = sip[0];
+
if (sip.Length == 1)
{
port = 25565;
@@ -80,47 +96,44 @@ namespace MinecraftClient
catch (FormatException) { port = 25565; }
}
+ if (!singlecommand)
+ {
+ if (Settings.AntiAFK_Enabled) { BotLoad(new ChatBots.AntiAFK(Settings.AntiAFK_Delay)); }
+ if (Settings.Hangman_Enabled) { BotLoad(new ChatBots.HangmanGame(Settings.Hangman_English)); }
+ if (Settings.Alerts_Enabled) { BotLoad(new ChatBots.Alerts()); }
+ if (Settings.ChatLog_Enabled) { BotLoad(new ChatBots.ChatLog(Settings.ChatLog_File.Replace("%username%", Settings.Username), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); }
+ if (Settings.PlayerLog_Enabled) { BotLoad(new ChatBots.PlayerListLogger(Settings.PlayerLog_Delay, Settings.PlayerLog_File.Replace("%username%", Settings.Username))); }
+ if (Settings.AutoRelog_Enabled) { BotLoad(new ChatBots.AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries)); }
+ if (Settings.ScriptScheduler_Enabled) { BotLoad(new ChatBots.ScriptScheduler(Settings.ScriptScheduler_TasksFile.Replace("%username%", Settings.Username))); }
+ if (Settings.RemoteCtrl_Enabled) { BotLoad(new ChatBots.RemoteControl()); }
+ }
+
try
{
- Console.WriteLine("Logging in...");
- client = new TcpClient(host, port);
+ client = ProxyHandler.newTcpClient(host, port);
client.ReceiveBufferSize = 1024 * 1024;
- handler.setClient(client);
- if (handler.Login(user, uuid, sessionID, host, port))
+ handler = Protocol.ProtocolHandler.getProtocolHandler(client, protocolversion, this);
+ Console.WriteLine("Version is supported.");
+ Console.WriteLine("Logging in...");
+
+ if (handler.Login())
{
- //Single command sending
if (singlecommand)
{
handler.SendChatMessage(command);
- Console.Write("Command ");
- Console.ForegroundColor = ConsoleColor.DarkGray;
- Console.Write(command);
- Console.ForegroundColor = ConsoleColor.Gray;
- Console.WriteLine(" sent.");
+ ConsoleIO.WriteLineFormatted("§7Command §8" + command + "§7 sent.", false);
Thread.Sleep(5000);
- handler.Disconnect("disconnect.quitting");
+ handler.Disconnect();
Thread.Sleep(1000);
}
else
{
+ foreach (ChatBot bot in scripts_on_hold) { bots.Add(bot); }
+ scripts_on_hold.Clear();
Console.WriteLine("Server was successfully joined.\nType '/quit' to leave the server.");
-
- //Command sending thread, allowing user input
- t_sender = new Thread(new ThreadStart(StartTalk));
- t_sender.Name = "CommandSender";
- t_sender.Start();
-
- //Data receiving thread, allowing text receiving
- t_updater = new Thread(new ThreadStart(Updater));
- t_updater.Name = "PacketHandler";
- t_updater.Start();
+ StartTalk();
}
}
- else
- {
- Console.WriteLine("Login failed.");
- if (!singlecommand && !handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, "Login failed.")) { Program.ReadLineReconnect(); }
- }
}
catch (SocketException)
{
@@ -136,14 +149,14 @@ namespace MinecraftClient
///
/// Allows the user to send chat messages, commands, and to leave the server.
- /// Will be automatically called on a separate Thread by StartClient()
///
private void StartTalk()
{
try
{
- //Needed if the player is dead
+ string text = "";
+ Thread.Sleep(500);
handler.SendRespawnPacket();
while (client.Client.Connected)
@@ -175,7 +188,7 @@ namespace MinecraftClient
}
else if (text.ToLower().StartsWith("/script "))
{
- handler.BotLoad(new Bots.Script(text.Substring(8)));
+ BotLoad(new ChatBots.Script(text.Substring(8)));
}
else if (text != "")
{
@@ -190,7 +203,7 @@ namespace MinecraftClient
}
else
{
- //Send the message splitted in several messages
+ //Send the message splitted into several messages
while (text.Length > 100)
{
handler.SendChatMessage(text.Substring(0, 100));
@@ -213,44 +226,84 @@ namespace MinecraftClient
catch (IOException) { }
}
- ///
- /// Receive the data (including chat messages) from the server, and keep the connection alive.
- /// Will be automatically called on a separate Thread by StartClient()
- ///
-
- private void Updater()
- {
- try
- {
- //handler.DebugDump();
- do
- {
- Thread.Sleep(100);
- } while (handler.Update());
- }
- catch (IOException) { }
- catch (SocketException) { }
- catch (ObjectDisposedException) { }
-
- if (!handler.HasBeenKicked)
- {
- ConsoleIO.WriteLine("Connection has been lost.");
- if (!handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "Connection has been lost.") && !Program.ReadLineReconnect()) { t_sender.Abort(); }
- }
- else if (Program.ReadLineReconnect()) { t_sender.Abort(); }
- }
-
///
/// Disconnect the client from the server
///
public void Disconnect()
{
- handler.Disconnect("disconnect.quitting");
+ foreach (ChatBot bot in bots)
+ if (bot is ChatBots.Script)
+ scripts_on_hold.Add((ChatBots.Script)bot);
+
+ handler.Disconnect();
+ handler.Dispose();
Thread.Sleep(1000);
- if (t_updater != null) { t_updater.Abort(); }
- if (t_sender != null) { t_sender.Abort(); }
+
if (client != null) { client.Close(); }
}
+
+ ///
+ /// Received some text from the server
+ ///
+ /// Text received
+
+ public void OnTextReceived(string text)
+ {
+ ConsoleIO.WriteLineFormatted(text, false);
+ foreach (ChatBot bot in bots)
+ bot.GetText(text);
+ }
+
+ ///
+ /// When connection has been lost
+ ///
+
+ public void OnConnectionLost(ChatBot.DisconnectReason reason, string message)
+ {
+ bool will_restart = false;
+
+ switch (reason)
+ {
+ case ChatBot.DisconnectReason.ConnectionLost:
+ message = "Connection has been lost.";
+ ConsoleIO.WriteLine(message);
+ break;
+
+ case ChatBot.DisconnectReason.InGameKick:
+ ConsoleIO.WriteLine("Disconnected by Server :");
+ ConsoleIO.WriteLineFormatted(message, true);
+ break;
+
+ case ChatBot.DisconnectReason.LoginRejected:
+ ConsoleIO.WriteLine("Login failed :");
+ ConsoleIO.WriteLineFormatted(message, true);
+ break;
+ }
+
+ foreach (ChatBot bot in bots)
+ will_restart |= bot.OnDisconnect(reason, message);
+
+ if (!will_restart) { Program.ReadLineReconnect(); }
+ }
+
+ ///
+ /// Called ~10 times per second by the protocol handler
+ ///
+
+ public void OnUpdate()
+ {
+ for (int i = 0; i < bots.Count; i++)
+ bots[i].Update();
+ }
+
+ ///
+ /// Send a chat message to the server
+ ///
+
+ public void SendChatMessage(string message)
+ {
+ handler.SendChatMessage(message);
+ }
}
}
diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj
index 384513bd..8add0d36 100644
--- a/MinecraftClient/MinecraftClient.csproj
+++ b/MinecraftClient/MinecraftClient.csproj
@@ -53,7 +53,7 @@
false
- resources\appicon.ico
+ Resources\AppIcon.ico
MinecraftClient.Program
@@ -70,14 +70,42 @@
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -103,7 +131,7 @@
-
+