using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Threading; using System.Text.RegularExpressions; using MinecraftClient.Inventory; using MinecraftClient.Mapping; 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 "ChatBots" folder. /// Override the methods you want for handling events: Initialize, Update, GetText. /// /// For testing your bot you can add it in McClient.cs (see comment at line ~199). /// Your bot will be loaded everytime MCC is started so that you can test/debug. /// /// Once your bot is fully written and tested, you can export it a standalone script. /// This way it can be loaded in newer MCC builds, without modifying MCC itself. /// See config/sample-script-with-chatbot.cs for a ChatBot script example. /// /// /// The virtual class containing anything you need for creating chat bots. /// public abstract class ChatBot { public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost, UserLogout }; //Handler will be automatically set on bot loading, don't worry about this public void SetHandler(McClient handler) { this._handler = handler; } protected void SetMaster(ChatBot master) { this.master = master; } protected void LoadBot(ChatBot bot) { Handler.BotUnLoad(bot); Handler.BotLoad(bot); } protected List GetLoadedChatBots() { return Handler.GetLoadedChatBots(); } protected void UnLoadBot(ChatBot bot) { Handler.BotUnLoad(bot); } private McClient _handler = null; private ChatBot master = null; private List registeredPluginChannels = new List(); private List registeredCommands = new List(); private McClient Handler { get { if (master != null) return master.Handler; if (_handler != null) return _handler; throw new InvalidOperationException(Translations.Get("exception.chatbot.init")); } } /* ================================================== */ /* Main methods to override for creating your bot */ /* ================================================== */ /// /// Anything you want to initialize your bot, will be called on load by MinecraftCom /// This method is called only once, whereas AfterGameJoined() is called once per server join. /// /// NOTE: Chat messages cannot be sent at this point in the login process. /// If you want to send a message when the bot is loaded, use AfterGameJoined. /// public virtual void Initialize() { } /// /// Called after the server has been joined successfully and chat messages are able to be sent. /// This method is called again after reconnecting to the server, whereas Initialize() is called only once. /// /// NOTE: This is not always right after joining the server - if the bot was loaded after logging /// in this is still called. /// public virtual void AfterGameJoined() { } /// /// 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) { } /// /// Any text sent by the server will be sent here by MinecraftCom (extended variant) /// /// /// You can use Json.ParseJson() to process the JSON string. /// /// Text from the server /// Raw JSON from the server. This parameter will be NULL on MC 1.5 or lower! public virtual void GetText(string text, string json) { } /// /// 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; } /// /// Called when a plugin channel message is received. /// The given channel must have previously been registered with RegisterPluginChannel. /// This can be used to communicate with server mods or plugins. See wiki.vg for more /// information about plugin channels: http://wiki.vg/Plugin_channel /// /// The name of the channel /// The payload for the message public virtual void OnPluginMessage(string channel, byte[] data) { } /// /// Called when properties for the Player entity are received from the server /// /// Dictionary of player properties public virtual void OnPlayerProperty(Dictionary prop) { } /// /// Called when server TPS are recalculated by MCC based on world time updates /// /// New estimated server TPS (between 0 and 20) public virtual void OnServerTpsUpdate(Double tps) { } /// /// Called when a time changed /// /// World age /// Time public virtual void OnTimeUpdate(long WorldAge, long TimeOfDay) { } /// /// Called when an entity moved nearby /// /// Entity with updated location public virtual void OnEntityMove(Mapping.Entity entity) { } /// /// Called after an internal MCC command has been performed /// /// MCC Command Name /// MCC Command Parameters /// MCC command result public virtual void OnInternalCommand(string commandName, string commandParams, string Result) { } /// /// Called when an entity spawned nearby /// /// New Entity public virtual void OnEntitySpawn(Mapping.Entity entity) { } /// /// Called when an entity despawns/dies nearby /// /// Entity wich has just disappeared public virtual void OnEntityDespawn(Mapping.Entity entity) { } /// /// Called when the player held item has changed /// /// New slot ID public virtual void OnHeldItemChange(byte slot) { } /// /// Called when the player health has been updated /// /// New player health /// New food level public virtual void OnHealthUpdate(float health, int food) { } /// /// Called when an explosion occurs on the server /// /// Explosion location /// Amount of blocks blown up public virtual void OnExplosion(Location explode, float strength, int recordcount) { } /// /// Called when experience updates /// /// Between 0 and 1 /// Level /// Total Experience public virtual void OnSetExperience(float Experiencebar, int Level, int TotalExperience) { } /// /// Called when the Game Mode has been updated for a player /// /// Player Name /// Player UUID /// New Game Mode (0: Survival, 1: Creative, 2: Adventure, 3: Spectator). public virtual void OnGamemodeUpdate(string playername, Guid uuid, int gamemode) { } /// /// Called when the Latency has been updated for a player /// /// Player Name /// Player UUID /// Latency. public virtual void OnLatencyUpdate(string playername, Guid uuid, int latency) { } /// /// Called when the Latency has been updated for a player /// /// Entity /// Player Name /// Player UUID /// Latency. public virtual void OnLatencyUpdate(Entity entity, string playername, Guid uuid, int latency) { } /// /// Called when a map was updated /// /// /// /// /// /// public virtual void OnMapData(int mapid, byte scale, bool trackingposition, bool locked, int iconcount) { } /// /// Called when tradeList is received from server /// /// Window ID /// List of trades. /// Contains Level, Experience, IsRegularVillager and CanRestock . public virtual void OnTradeList(int windowID, List trades, VillagerInfo villagerInfo) { } /// /// Called when received a title from the server /// 0 = set title, 1 = set subtitle, 3 = set action bar, 4 = set times and display, 4 = hide, 5 = reset /// title text /// suntitle text /// action bar text /// Fade In /// Stay /// Fade Out /// json text public virtual void OnTitle(int action, string titletext, string subtitletext, string actionbartext, int fadein, int stay, int fadeout, string json) { } /// /// Called when an entity equipped /// /// Entity /// Equipment slot. 0: main hand, 1: off hand, 2–5: armor slot (2: boots, 3: leggings, 4: chestplate, 5: helmet) /// Item) public virtual void OnEntityEquipment(Entity entity, int slot, Item item) { } /// /// Called when an entity has effect applied /// /// entity ID /// effect id /// effect amplifier /// effect duration /// effect flags public virtual void OnEntityEffect(Entity entity, Effects effect, int amplifier, int duration, byte flags) { } /// /// Called when a scoreboard objective updated /// /// objective name /// 0 to create the scoreboard. 1 to remove the scoreboard. 2 to update the display text. /// Only if mode is 0 or 2. The text to be displayed for the score /// Only if mode is 0 or 2. 0 = "integer", 1 = "hearts". public virtual void OnScoreboardObjective(string objectivename, byte mode, string objectivevalue, int type, string json) { } /// /// Called when a scoreboard updated /// /// The entity whose score this is. For players, this is their username; for other entities, it is their UUID. /// 0 to create/update an item. 1 to remove an item. /// The name of the objective the score belongs to /// The score to be displayed next to the entry. Only sent when Action does not equal 1. public virtual void OnUpdateScore(string entityname, byte action, string objectivename, int value) { } /// /// Called when an inventory/container was updated by server /// /// public virtual void OnInventoryUpdate(int inventoryId) { } /// /// Called when a container was opened /// /// public virtual void OnInventoryOpen(int inventoryId) { } /// /// Called when a container was closed /// /// public virtual void OnInventoryClose(int inventoryId) { } /// /// Called when a player joined the game /// /// UUID of the player /// Name of the player public virtual void OnPlayerJoin(Guid uuid, string name) { } /// /// Called when a player left the game /// /// UUID of the player /// Name of the player public virtual void OnPlayerLeave(Guid uuid, string name) { } /// /// Called when the player deaths /// public virtual void OnDeath() { } /// /// Called when the player respawns /// public virtual void OnRespawn() { } /// /// Called when the health of an entity changed /// /// Entity /// The health of the entity public virtual void OnEntityHealth(Entity entity, float health) { } /// /// Called when the metadata of an entity changed /// /// Entity /// The metadata of the entity /// Ptotocol version public virtual void OnEntityMetadata(Entity entity, Dictionary metadata) { } /// /// Called when a network packet received or sent /// /// /// You need to enable this event by calling with True before you can use this event /// /// Packet ID /// A copy of Packet Data /// The packet is login phase or playing phase /// The packet is received from server or sent by client public virtual void OnNetworkPacket(int packetID, List packetData, bool isLogin, bool isInbound) { } /* =================================================================== */ /* ToolBox - Methods below might be useful while creating your bot. */ /* You should not need to interact with other classes of the program. */ /* All the methods in this ChatBot class should do the job for you. */ /* =================================================================== */ /// /// Send text to the server. Can be anything such as chat messages or commands /// /// Text to send to the server /// Bypass send queue (Deprecated, still there for compatibility purposes but ignored) /// TRUE if successfully sent (Deprectated, always returns TRUE for compatibility purposes with existing scripts) protected bool SendText(string text, bool sendImmediately = false) { LogToConsole("Sending '" + text + "'"); Handler.SendText(text); return true; } /// /// Perform an internal MCC command (not a server command, use SendText() instead for that!) /// /// The command to process /// Local variables passed along with the command /// TRUE if the command was indeed an internal MCC command protected bool PerformInternalCommand(string command, Dictionary localVars = null) { string temp = ""; return Handler.PerformInternalCommand(command, ref temp, localVars); } /// /// Perform an internal MCC command (not a server command, use SendText() instead for that!) /// /// The command to process /// May contain a confirmation or error message after processing the command, or "" otherwise. /// Local variables passed along with the command /// TRUE if the command was indeed an internal MCC command protected bool PerformInternalCommand(string command, ref string response_msg, Dictionary localVars = null) { return Handler.PerformInternalCommand(command, ref response_msg, localVars); } /// /// Remove color codes ("§c") from a text message received from the server /// public 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. /// public 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 if 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 (String.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); //User-defined regex for private chat messages if (Settings.ChatFormat_Private != null) { Match regexMatch = Settings.ChatFormat_Private.Match(text); if (regexMatch.Success && regexMatch.Groups.Count >= 3) { sender = regexMatch.Groups[1].Value; message = regexMatch.Groups[2].Value; return IsValidName(sender); } } //Built-in detection routine for private messages if (Settings.ChatFormat_Builtins) { string[] tmp = text.Split(' '); try { //Detect vanilla /tell messages //Someone whispers message (MC 1.5) //Someone whispers to you: message (MC 1.7) if (tmp.Length > 2 && tmp[1] == "whispers") { if (tmp.Length > 4 && tmp[2] == "to" && tmp[3] == "you:") { message = text.Substring(tmp[0].Length + 18); //MC 1.7 } else message = text.Substring(tmp[0].Length + 10); //MC 1.5 sender = tmp[0]; return IsValidName(sender); } //Detect Essentials (Bukkit) /m messages //[Someone -> me] message //[~Someone -> me] message else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "->" && (tmp[2].ToLower() == "me]" || tmp[2].ToLower() == "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); } //Detect Modified server messages. /m //[Someone @ me] message else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "@" && (tmp[2].ToLower() == "me]" || tmp[2].ToLower() == "moi]")) //'me' is replaced by 'moi' in french servers { message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 0); sender = tmp[0].Substring(1); if (sender[0] == '~') { sender = sender.Substring(1); } return IsValidName(sender); } //Detect Essentials (Bukkit) /me messages with some custom prefix //[Prefix] [Someone -> me] message //[Prefix] [~Someone -> me] message else if (text[0] == '[' && tmp[0][tmp[0].Length - 1] == ']' && tmp[1][0] == '[' && tmp.Length > 4 && tmp[2] == "->" && (tmp[3].ToLower() == "me]" || tmp[3].ToLower() == "moi]")) { message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[3].Length + 1); sender = tmp[1].Substring(1); if (sender[0] == '~') { sender = sender.Substring(1); } return IsValidName(sender); } //Detect Essentials (Bukkit) /me messages with some custom rank //[Someone [rank] -> me] message //[~Someone [rank] -> me] message else if (text[0] == '[' && tmp.Length > 3 && tmp[2] == "->" && (tmp[3].ToLower() == "me]" || tmp[3].ToLower() == "moi]")) { message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[2].Length + 1); sender = tmp[0].Substring(1); if (sender[0] == '~') { sender = sender.Substring(1); } return IsValidName(sender); } //Detect HeroChat PMsend //From Someone: message else if (text.StartsWith("From ")) { sender = text.Substring(5).Split(':')[0]; message = text.Substring(text.IndexOf(':') + 2); return IsValidName(sender); } else return false; } catch (IndexOutOfRangeException) { /* Not an expected chat format */ } catch (ArgumentOutOfRangeException) { /* Same here */ } } return false; } /// /// Returns true if 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) { if (String.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); //User-defined regex for public chat messages if (Settings.ChatFormat_Public != null) { Match regexMatch = Settings.ChatFormat_Public.Match(text); if (regexMatch.Success && regexMatch.Groups.Count >= 3) { sender = regexMatch.Groups[1].Value; message = regexMatch.Groups[2].Value; return IsValidName(sender); } } //Built-in detection routine for public messages if (Settings.ChatFormat_Builtins) { string[] tmp = text.Split(' '); //Detect vanilla/factions Messages // message //<*Faction Someone> message //<*Faction Someone>: message //<*Faction ~Nicknamed>: message if (text[0] == '<') { try { text = text.Substring(1); string[] tmp2 = text.Split('>'); sender = tmp2[0]; message = text.Substring(sender.Length + 2); if (message.Length > 1 && message[0] == ' ') { message = message.Substring(1); } tmp2 = sender.Split(' '); sender = tmp2[tmp2.Length - 1]; if (sender[0] == '~') { sender = sender.Substring(1); } return IsValidName(sender); } catch (IndexOutOfRangeException) { /* Not a vanilla/faction message */ } catch (ArgumentOutOfRangeException) { /* Same here */ } } //Detect HeroChat Messages //Public chat messages //[Channel] [Rank] User: Message else if (text[0] == '[' && text.Contains(':') && tmp.Length > 2) { try { int name_end = text.IndexOf(':'); int name_start = text.Substring(0, name_end).LastIndexOf(']') + 2; sender = text.Substring(name_start, name_end - name_start); message = text.Substring(name_end + 2); return IsValidName(sender); } catch (IndexOutOfRangeException) { /* Not a herochat message */ } catch (ArgumentOutOfRangeException) { /* Same here */ } } //Detect (Unknown Plugin) Messages //**Faction User : Message else if (text[0] == '*' && text.Length > 1 && text[1] != ' ' && text.Contains('<') && text.Contains('>') && text.Contains(' ') && text.Contains(':') && text.IndexOf('*') < text.IndexOf('<') && text.IndexOf('<') < text.IndexOf('>') && text.IndexOf('>') < text.IndexOf(' ') && text.IndexOf(' ') < text.IndexOf(':')) { try { string prefix = tmp[0]; string user = tmp[1]; string semicolon = tmp[2]; if (prefix.All(c => char.IsLetterOrDigit(c) || new char[] { '*', '<', '>', '_' }.Contains(c)) && semicolon == ":") { message = text.Substring(prefix.Length + user.Length + 4); return IsValidName(user); } } catch (IndexOutOfRangeException) { /* Not a message */ } catch (ArgumentOutOfRangeException) { /* Same here */ } } } return false; } /// /// Returns true if the text passed is a teleport request (Essentials) /// /// Text to parse /// Will contain the sender's username, if it's a teleport request /// Returns true if the text is a teleport request protected static bool IsTeleportRequest(string text, ref string sender) { if (String.IsNullOrEmpty(text)) return false; text = GetVerbatim(text); //User-defined regex for teleport requests if (Settings.ChatFormat_TeleportRequest != null) { Match regexMatch = Settings.ChatFormat_TeleportRequest.Match(text); if (regexMatch.Success && regexMatch.Groups.Count >= 2) { sender = regexMatch.Groups[1].Value; return IsValidName(sender); } } //Built-in detection routine for teleport requests if (Settings.ChatFormat_Builtins) { string[] tmp = text.Split(' '); //Detect Essentials teleport requests, prossibly with //nicknamed names or other modifications such as HeroChat if (text.EndsWith("has requested to teleport to you.") || text.EndsWith("has requested that you teleport to them.")) { // Username has requested... //[Rank] Username has requested... if (((tmp[0].StartsWith("<") && tmp[0].EndsWith(">")) || (tmp[0].StartsWith("[") && tmp[0].EndsWith("]"))) && tmp.Length > 1) sender = tmp[1]; //Username has requested... else sender = tmp[0]; //~Username has requested... if (sender.Length > 1 && sender[0] == '~') sender = sender.Substring(1); //Final check on username validity return IsValidName(sender); } } return false; } /// /// Write some text in the console. Nothing will be sent to the server. /// /// Log text to write protected void LogToConsole(object text) { if (_handler == null || master == null) ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", this.GetType().Name, text)); else Handler.Log.Info(String.Format("[{0}] {1}", this.GetType().Name, text)); string logfile = Settings.ExpandVars(Settings.chatbotLogFile); if (!String.IsNullOrEmpty(logfile)) { if (!File.Exists(logfile)) { try { Directory.CreateDirectory(Path.GetDirectoryName(logfile)); } catch { return; /* Invalid path or access denied */ } try { File.WriteAllText(logfile, ""); } catch { return; /* Invalid file name or access denied */ } } File.AppendAllLines(logfile, new string[] { GetTimestamp() + ' ' + text }); } } /// /// Write some text in the console, but only if DebugMessages is enabled in INI file. Nothing will be sent to the server. /// /// Debug log text to write protected void LogDebugToConsole(object text) { if (Settings.DebugMessages) LogToConsole(text); } /// /// Write the translated text in the console by giving a translation key. Nothing will be sent to the server. /// /// Translation key /// protected void LogToConsoleTranslated(string key, params object[] args) { LogToConsole(Translations.TryGet(key, args)); } /// /// Write the translated text in the console by giving a translation key, but only if DebugMessages is enabled in INI file. Nothing will be sent to the server. /// /// Translation key /// protected void LogDebugToConsoleTranslated(string key, params object[] args) { LogDebugToConsole(Translations.TryGet(key, args)); } /// /// Disconnect from the server and restart the program /// It will unload and reload all the bots and then reconnect to the server /// /// In case of failure, maximum extra attempts before aborting /// Optional delay, in seconds, before restarting protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0) { if (Settings.DebugMessages) ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", this.GetType().Name)); McClient.ReconnectionAttemptsLeft = ExtraAttempts; Program.Restart(delaySeconds); } /// /// Disconnect from the server and exit the program /// protected void DisconnectAndExit() { Program.Exit(); } /// /// Unload the chatbot, and release associated memory. /// protected void UnloadBot() { foreach (string cmdName in registeredCommands) { Handler.UnregisterCommand(cmdName); } Handler.BotUnLoad(this); } /// /// Send a private message to a player /// /// Player name /// Message protected void SendPrivateMessage(string player, string message) { SendText(String.Format("/{0} {1} {2}", Settings.PrivateMsgsCmdName, player, message)); } /// /// Run a script from a file using a Scripting bot /// /// File name /// Player name to send error messages, if applicable /// Local variables for use in the Script protected void RunScript(string filename, string playername = null, Dictionary localVars = null) { Handler.BotLoad(new ChatBots.Script(filename, playername, localVars)); } /// /// Load an additional ChatBot /// /// ChatBot to load protected void BotLoad(ChatBot chatBot) { Handler.BotLoad(chatBot); } /// /// Check whether Terrain and Movements is enabled. /// /// Enable status. public bool GetTerrainEnabled() { return Handler.GetTerrainEnabled(); } /// /// Enable or disable Terrain and Movements. /// Please note that Enabling will be deferred until next relog, respawn or world change. /// /// Enabled /// TRUE if the setting was applied immediately, FALSE if delayed. public bool SetTerrainEnabled(bool enabled) { return Handler.SetTerrainEnabled(enabled); } /// /// Get entity handling status /// /// /// Entity Handling cannot be enabled in runtime (or after joining server) public bool GetEntityHandlingEnabled() { return Handler.GetEntityHandlingEnabled(); } /// /// start Sneaking /// protected bool Sneak(bool on) { return SendEntityAction(on ? Protocol.EntityActionType.StartSneaking : Protocol.EntityActionType.StopSneaking); } /// /// Send Entity Action /// private bool SendEntityAction(Protocol.EntityActionType entityAction) { return Handler.SendEntityAction(entityAction); } /// /// Attempt to dig a block at the specified location /// /// Location of block to dig /// Also perform the "arm swing" animation /// Also look at the block before digging protected bool DigBlock(Location location, bool swingArms = true, bool lookAtBlock = true) { return Handler.DigBlock(location, swingArms, lookAtBlock); } /// /// SetSlot /// protected void SetSlot(int slotNum) { Handler.ChangeSlot((short)slotNum); } /// /// Get the current Minecraft World /// /// Minecraft world or null if associated setting is disabled protected Mapping.World GetWorld() { if (GetTerrainEnabled()) return Handler.GetWorld(); return null; } /// /// Get all Entities /// /// All Entities protected Dictionary GetEntities() { return Handler.GetEntities(); } /// /// Get all players Latency /// /// All players latency protected Dictionary GetPlayersLatency() { return Handler.GetPlayersLatency(); } /// /// Get the current location of the player (Feet location) /// /// Minecraft world or null if associated setting is disabled protected Mapping.Location GetCurrentLocation() { return Handler.GetCurrentLocation(); } /// /// Move to the specified location /// /// Location to reach /// Allow possible but unsafe locations thay may hurt the player: lava, cactus... /// Allow non-vanilla teleport instead of computing path, but may cause invalid moves and/or trigger anti-cheat plugins /// True if a path has been found protected bool MoveToLocation(Mapping.Location location, bool allowUnsafe = false, bool allowDirectTeleport = false) { return Handler.MoveTo(location, allowUnsafe, allowDirectTeleport); } /// /// Look at the specified location /// /// Location to look at protected void LookAtLocation(Mapping.Location location) { Handler.UpdateLocation(Handler.GetCurrentLocation(), location); } /// /// Get a Y-M-D h:m:s timestamp representing the current system date and time /// protected static string GetTimestamp() { DateTime time = DateTime.Now; return String.Format("{0}-{1}-{2} {3}:{4}:{5}", time.Year.ToString("0000"), time.Month.ToString("00"), time.Day.ToString("00"), time.Hour.ToString("00"), time.Minute.ToString("00"), time.Second.ToString("00")); } /// /// Load entries from a file as a string array, removing duplicates and empty lines /// /// File to load /// The string array or an empty array if failed to load the file protected string[] LoadDistinctEntriesFromFile(string file) { if (File.Exists(file)) { //Read all lines from file, remove lines with no text, convert to lowercase, //remove duplicate entries, convert to a string array, and return the result. return File.ReadAllLines(file, Encoding.UTF8) .Where(line => !String.IsNullOrWhiteSpace(line)) .Select(line => line.ToLower()) .Distinct().ToArray(); } else { LogToConsole("File not found: " + System.IO.Path.GetFullPath(file)); return new string[0]; } } /// /// Return the Server Port where the client is connected to /// /// Server Port where the client is connected to protected int GetServerPort() { return Handler.GetServerPort(); } /// /// Return the Server Host where the client is connected to /// /// Server Host where the client is connected to protected string GetServerHost() { return Handler.GetServerHost(); } /// /// Return the Username of the current account /// /// Username of the current account protected string GetUsername() { return Handler.GetUsername(); } /// /// Return the Gamemode of the current account /// /// Username of the current account protected int GetGamemode() { return Handler.GetGamemode(); } /// /// Return the UserUUID of the current account /// /// UserUUID of the current account protected string GetUserUUID() { return Handler.GetUserUUID(); } /// /// Return the list of currently online players /// /// List of online players protected string[] GetOnlinePlayers() { return Handler.GetOnlinePlayers(); } /// /// Get a dictionary of online player names and their corresponding UUID /// /// /// dictionary of online player whereby /// UUID represents the key /// playername represents the value protected Dictionary GetOnlinePlayersWithUUID() { return Handler.GetOnlinePlayersWithUUID(); } /// /// Registers the given plugin channel for use by this chatbot. /// /// The name of the channel to register protected void RegisterPluginChannel(string channel) { this.registeredPluginChannels.Add(channel); Handler.RegisterPluginChannel(channel, this); } /// /// Unregisters the given plugin channel, meaning this chatbot can no longer use it. /// /// The name of the channel to unregister protected void UnregisterPluginChannel(string channel) { this.registeredPluginChannels.RemoveAll(chan => chan == channel); Handler.UnregisterPluginChannel(channel, this); } /// /// Sends the given plugin channel message to the server, if the channel has been registered. /// See http://wiki.vg/Plugin_channel for more information about plugin channels. /// /// The channel to send the message on. /// The data to send. /// Should the message be sent even if it hasn't been registered by the server or this bot? (Some Minecraft channels aren't registered) /// Whether the message was successfully sent. False if there was a network error or if the channel wasn't registered. protected bool SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false) { if (!sendEvenIfNotRegistered) { if (!this.registeredPluginChannels.Contains(channel)) { return false; } } return Handler.SendPluginChannelMessage(channel, data, sendEvenIfNotRegistered); } /// /// Get server current TPS (tick per second) /// /// tps protected Double GetServerTPS() { return Handler.GetServerTPS(); } /// /// Interact with an entity /// /// /// 0: interact, 1: attack, 2: interact at /// Hand.MainHand or Hand.OffHand /// TRUE in case of success protected bool InteractEntity(int EntityID, int type, Hand hand = Hand.MainHand) { return Handler.InteractEntity(EntityID, type, hand); } /// /// Give Creative Mode items into regular/survival Player Inventory /// /// (obviously) requires to be in creative mode /// /// Destination inventory slot /// Item type /// Item count /// TRUE if item given successfully protected bool CreativeGive(int slot, ItemType itemType, int count, Dictionary nbt = null) { return Handler.DoCreativeGive(slot, itemType, count, nbt); } /// /// Plays animation (Player arm swing) /// /// Hand.MainHand or Hand.OffHand /// TRUE if animation successfully done public bool SendAnimation(Hand hand = Hand.MainHand) { return Handler.DoAnimation((int)hand); } /// /// Use item currently in the player's hand (active inventory bar slot) /// /// TRUE if successful protected bool UseItemInHand() { return Handler.UseItemOnHand(); } /// /// Check inventory handling enable status /// /// TRUE if inventory handling is enabled public bool GetInventoryEnabled() { return Handler.GetInventoryEnabled(); } /// /// Place the block at hand in the Minecraft world /// /// Location to place block to /// Block face (e.g. Direction.Down when clicking on the block below to place this block) /// Hand.MainHand or Hand.OffHand /// TRUE if successfully placed public bool SendPlaceBlock(Location location, Direction blockFace, Hand hand = Hand.MainHand) { return Handler.PlaceBlock(location, blockFace, hand); } /// /// Get the player's inventory. Do not write to it, will not have any effect server-side. /// /// Player inventory protected Container GetPlayerInventory() { Container container = Handler.GetPlayerInventory(); return container == null ? null : new Container(container.ID, container.Type, container.Title, container.Items); } /// /// Get all inventories, player and container(s). Do not write to them. Will not have any effect server-side. /// /// All inventories public Dictionary GetInventories() { return Handler.GetInventories(); } /// /// Perform inventory action /// /// Inventory ID /// Slot ID /// Action Type /// TRUE in case of success protected bool WindowAction(int inventoryId, int slot, WindowActionType actionType) { return Handler.DoWindowAction(inventoryId, slot, actionType); } /// /// Get inventory action helper /// /// Inventory Container /// ItemMovingHelper instance protected ItemMovingHelper GetItemMovingHelper(Container container) { return new ItemMovingHelper(container, Handler); } /// /// Change player selected hotbar slot /// /// 0-8 /// True if success protected bool ChangeSlot(short slot) { return Handler.ChangeSlot(slot); } /// /// Get current player selected hotbar slot /// /// 0-8 protected byte GetCurrentSlot() { return Handler.GetCurrentSlot(); } /// /// Clean all inventory /// /// TRUE if the uccessfully clear protected bool ClearInventories() { return Handler.ClearInventories(); } /// /// Update sign text /// /// sign location /// text one /// text two /// text three /// text1 four protected bool UpdateSign(Location location, string line1, string line2, string line3, string line4) { return Handler.UpdateSign(location, line1, line2, line3, line4); } /// /// Selects villager trade /// /// Trade slot to select, starts at 0. protected bool SelectTrade(int selectedSlot) { return Handler.SelectTrade(selectedSlot); } /// /// Update command block /// /// command block location /// command /// command block mode /// command block flags protected bool UpdateCommandBlock(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags) { return Handler.UpdateCommandBlock(location, command, mode, flags); } /// /// Register a command in command prompt. Command will be automatically unregistered when unloading ChatBot /// /// Name of the command /// Description/usage of the command /// Method for handling the command /// True if successfully registered protected bool RegisterChatBotCommand(string cmdName, string cmdDesc, string cmdUsage, CommandRunner callback) { bool result = Handler.RegisterCommand(cmdName, cmdDesc, cmdUsage, callback); if (result) registeredCommands.Add(cmdName.ToLower()); return result; } /// /// Close a opened inventory /// /// /// True if success protected bool CloseInventory(int inventoryID) { return Handler.CloseInventory(inventoryID); } /// /// Get max length for chat messages /// /// Max length, in characters protected int GetMaxChatMessageLength() { return Handler.GetMaxChatMessageLength(); } /// /// Respawn player /// protected bool Respawn() { if (Handler.GetHealth() <= 0) return Handler.SendRespawnPacket(); else return false; } /// /// Enable or disable network packet event calling. If you want to capture every packet including login phase, please enable this in /// /// /// Enable this may increase memory usage. /// /// protected void SetNetworkPacketEventEnabled(bool enabled) { Handler.SetNetworkPacketCaptureEnabled(enabled); } /// /// Get the minecraft protcol number currently in use /// /// Protcol number protected int GetProtocolVersion() { return Handler.GetProtocolVersion(); } /// /// Command runner definition. /// Returned string will be the output of the command /// /// Full command /// Arguments in the command /// Command result to display to the user public delegate string CommandRunner(string command, string[] args); /// /// Command class with constructor for creating command for ChatBots. /// public class ChatBotCommand : Command { public CommandRunner Runner; private readonly string _cmdName; private readonly string _cmdDesc; private readonly string _cmdUsage; public override string CmdName { get { return _cmdName; } } public override string CmdUsage { get { return _cmdUsage; } } public override string CmdDesc { get { return _cmdDesc; } } public override string Run(McClient handler, string command, Dictionary localVars) { return this.Runner(command, getArgs(command)); } /// /// ChatBotCommand Constructor /// /// Name of the command /// Description of the command. Support tranlation. /// Usage of the command /// Method for handling the command public ChatBotCommand(string cmdName, string cmdDesc, string cmdUsage, CommandRunner callback) { this._cmdName = cmdName; this._cmdDesc = cmdDesc; this._cmdUsage = cmdUsage; this.Runner = callback; } } } }