using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.
///
public class McTcpClient : IMinecraftComHandler
{
private static List cmd_names = new List();
private static Dictionary cmds = new Dictionary();
private List bots = new List();
private readonly Dictionary onlinePlayers = new Dictionary();
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;
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;
IMinecraftCom handler;
Thread cmdprompt;
///
/// Starts the main chat client
///
/// The chosen username of a premium Minecraft Account
/// The player's UUID for online-mode authentication
/// A valid sessionID obtained after logging in
/// The server IP
/// The server port to use
/// Minecraft protocol version to use
public McTcpClient(string username, string uuid, string sessionID, int protocolversion, string server_ip, ushort port)
{
StartClient(username, uuid, sessionID, server_ip, port, protocolversion, false, "");
}
///
/// Starts the main chat client in single command sending mode
///
/// The chosen username of a premium Minecraft Account
/// The player's UUID for online-mode authentication
/// A valid sessionID obtained after logging in
/// The server IP
/// The server port to use
/// Minecraft protocol version to use
/// The text or command to send.
public McTcpClient(string username, string uuid, string sessionID, string server_ip, ushort port, int protocolversion, string command)
{
StartClient(username, uuid, sessionID, server_ip, port, protocolversion, true, command);
}
///
/// Starts the main chat client, wich will login to the server using the MinecraftCom class.
///
/// The chosen username of a premium Minecraft Account
/// A valid sessionID obtained with MinecraftCom.GetLogin()
/// The server IP
/// The server port to use
/// Minecraft protocol version to use
/// The player's UUID for online-mode authentication
/// 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_ip, ushort port, int protocolversion, bool singlecommand, string command)
{
bool retry = false;
this.sessionid = sessionID;
this.uuid = uuid;
this.username = user;
this.host = server_ip;
this.port = port;
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.ExpandVars(Settings.ChatLog_File), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); }
if (Settings.PlayerLog_Enabled) { BotLoad(new ChatBots.PlayerListLogger(Settings.PlayerLog_Delay, Settings.ExpandVars(Settings.PlayerLog_File))); }
if (Settings.AutoRelog_Enabled) { BotLoad(new ChatBots.AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries)); }
if (Settings.ScriptScheduler_Enabled) { BotLoad(new ChatBots.ScriptScheduler(Settings.ExpandVars(Settings.ScriptScheduler_TasksFile))); }
if (Settings.RemoteCtrl_Enabled) { BotLoad(new ChatBots.RemoteControl()); }
if (Settings.AutoRespond_Enabled) { BotLoad(new ChatBots.AutoRespond(Settings.AutoRespond_Matches)); }
}
try
{
client = ProxyHandler.newTcpClient(host, port);
client.ReceiveBufferSize = 1024 * 1024;
handler = Protocol.ProtocolHandler.getProtocolHandler(client, protocolversion, this);
Console.WriteLine("Version is supported.\nLogging in...");
try
{
if (handler.Login())
{
if (singlecommand)
{
handler.SendChatMessage(command);
ConsoleIO.WriteLineFormatted("§7Command §8" + command + "§7 sent.");
Thread.Sleep(5000);
handler.Disconnect();
Thread.Sleep(1000);
}
else
{
foreach (ChatBot bot in scripts_on_hold)
bot.SetHandler(this);
bots.AddRange(scripts_on_hold);
scripts_on_hold.Clear();
Console.WriteLine("Server was successfully joined.\nType '"
+ (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar)
+ "quit' to leave the server.");
cmdprompt = new Thread(new ThreadStart(CommandPrompt));
cmdprompt.Name = "MCC Command prompt";
cmdprompt.Start();
}
}
}
catch (Exception e)
{
ConsoleIO.WriteLineFormatted("§8" + e.Message);
Console.WriteLine("Failed to join this server.");
retry = true;
}
}
catch (SocketException e)
{
ConsoleIO.WriteLineFormatted("§8" + e.Message);
Console.WriteLine("Failed to connect to this IP.");
retry = true;
}
if (retry)
{
if (AttemptsLeft > 0)
{
ConsoleIO.WriteLogLine("Waiting 5 seconds (" + AttemptsLeft + " attempts left)...");
Thread.Sleep(5000); AttemptsLeft--; Program.Restart();
}
else if (!singlecommand && Settings.interactiveMode)
{
Program.HandleFailure();
}
}
}
///
/// Allows the user to send chat messages, commands, and to leave the server.
///
private void CommandPrompt()
{
try
{
string text = "";
Thread.Sleep(500);
handler.SendRespawnPacket();
while (client.Client.Connected)
{
text = ConsoleIO.ReadLine();
if (ConsoleIO.basicIO && text.Length > 0 && text[0] == (char)0x00)
{
//Process a request from the GUI
string[] command = text.Substring(1).Split((char)0x00);
switch (command[0].ToLower())
{
case "autocomplete":
if (command.Length > 1) { ConsoleIO.WriteLine((char)0x00 + "autocomplete" + (char)0x00 + handler.AutoComplete(command[1])); }
else Console.WriteLine((char)0x00 + "autocomplete" + (char)0x00);
break;
}
}
else
{
text = text.Trim();
if (text.Length > 0)
{
if (Settings.internalCmdChar == ' ' || text[0] == Settings.internalCmdChar)
{
string response_msg = "";
string command = Settings.internalCmdChar == ' ' ? text : text.Substring(1);
if (!PerformInternalCommand(Settings.ExpandVars(command), ref response_msg) && Settings.internalCmdChar == '/')
{
SendText(text);
}
else if (response_msg.Length > 0)
{
ConsoleIO.WriteLineFormatted("§8MCC: " + response_msg);
}
}
else SendText(text);
}
}
}
}
catch (IOException) { }
}
///
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
///
/// The command
/// Set to true if command was sent by the user using the command prompt
/// May contain a confirmation or error message after processing the command, or "" otherwise.
/// TRUE if the command was indeed an internal MCC command
public bool PerformInternalCommand(string command, ref string response_msg)
{
/* Load commands from the 'Commands' namespace */
if (cmds.Count == 0)
{
Type[] cmds_classes = Program.GetTypesInNamespace("MinecraftClient.Commands");
foreach (Type type in cmds_classes)
{
if (type.IsSubclassOf(typeof(Command)))
{
try
{
Command cmd = (Command)Activator.CreateInstance(type);
cmds[cmd.CMDName.ToLower()] = cmd;
cmd_names.Add(cmd.CMDName.ToLower());
foreach (string alias in cmd.getCMDAliases())
cmds[alias.ToLower()] = cmd;
}
catch (Exception e)
{
ConsoleIO.WriteLine(e.Message);
}
}
}
}
/* Process the provided command */
string command_name = command.Split(' ')[0].ToLower();
if (command_name == "help")
{
if (Command.hasArg(command))
{
string help_cmdname = Command.getArgs(command)[0].ToLower();
if (help_cmdname == "help")
{
response_msg = "help : show brief help about a command.";
}
else if (cmds.ContainsKey(help_cmdname))
{
response_msg = cmds[help_cmdname].CMDDesc;
}
else response_msg = "Unknown command '" + command_name + "'. Use 'help' for command list.";
}
else response_msg = "help . Available commands: " + String.Join(", ", cmd_names.ToArray());
}
else if (cmds.ContainsKey(command_name))
{
response_msg = cmds[command_name].Run(this, command);
}
else
{
response_msg = "Unknown command '" + command_name + "'. Use '" + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + "help' for help.";
return false;
}
return true;
}
///
/// Disconnect the client from the server
///
public void Disconnect()
{
foreach (ChatBot bot in bots)
if (bot is ChatBots.Script)
scripts_on_hold.Add((ChatBots.Script)bot);
if (handler != null)
{
handler.Disconnect();
handler.Dispose();
}
if (cmdprompt != null)
cmdprompt.Abort();
Thread.Sleep(1000);
if (client != null)
client.Close();
}
///
/// Received some text from the server
///
/// Text received
public void OnTextReceived(string text)
{
ConsoleIO.WriteLineFormatted(text, false);
for (int i = 0; i < bots.Count; i++)
{
try
{
bots[i].GetText(text);
}
catch (Exception e)
{
if (!(e is ThreadAbortException))
{
ConsoleIO.WriteLineFormatted("§8GetText: Got error from " + bots[i].ToString() + ": " + e.ToString());
}
else throw; //ThreadAbortException should not be caught
}
}
}
///
/// 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);
break;
case ChatBot.DisconnectReason.LoginRejected:
ConsoleIO.WriteLine("Login failed :");
ConsoleIO.WriteLineFormatted(message);
break;
}
foreach (ChatBot bot in bots)
will_restart |= bot.OnDisconnect(reason, message);
if (!will_restart)
Program.HandleFailure();
}
///
/// Called ~10 times per second by the protocol handler
///
public void OnUpdate()
{
for (int i = 0; i < bots.Count; i++)
{
try
{
bots[i].Update();
}
catch (Exception e)
{
if (!(e is ThreadAbortException))
{
ConsoleIO.WriteLineFormatted("§8Update: Got error from " + bots[i].ToString() + ": " + e.ToString());
}
else throw; //ThreadAbortException should not be caught
}
}
}
///
/// Send a chat message or command to the server
///
/// Text to send to the server
/// True if the text was sent with no error
public bool SendText(string text)
{
if (text.Length > 100) //Message is too long?
{
if (text[0] == '/')
{
//Send the first 100 chars of the command
text = text.Substring(0, 100);
return handler.SendChatMessage(text);
}
else
{
//Send the message splitted into several messages
while (text.Length > 100)
{
handler.SendChatMessage(text.Substring(0, 100));
text = text.Substring(100, text.Length - 100);
if (Settings.splitMessageDelay.TotalSeconds > 0)
Thread.Sleep(Settings.splitMessageDelay);
}
return handler.SendChatMessage(text);
}
}
else return handler.SendChatMessage(text);
}
///
/// Allow to respawn after death
///
/// True if packet successfully sent
public bool SendRespawnPacket()
{
return handler.SendRespawnPacket();
}
///
/// Triggered when a new player joins the game
///
/// UUID of the player
/// Name of the player
public void OnPlayerJoin(Guid uuid, string name)
{
//Ignore TabListPlus placeholders
if (name.StartsWith("0000tab#"))
return;
lock (onlinePlayers)
{
onlinePlayers[uuid] = name;
}
}
///
/// Triggered when a player has left the game
///
/// UUID of the player
public void OnPlayerLeave(Guid uuid)
{
lock (onlinePlayers)
{
onlinePlayers.Remove(uuid);
}
}
///
/// Get a set of online player names
///
/// Online player names
public string[] GetOnlinePlayers()
{
lock (onlinePlayers)
{
return onlinePlayers.Values.Distinct().ToArray();
}
}
}
}