Added MCC Source Code v1.5.2

Minecraft Console Client is now on GitHub!
This commit is contained in:
ORelio 2013-07-18 09:27:19 +02:00
parent bb02f5b76d
commit e6f2ef4e4f
16 changed files with 3116 additions and 0 deletions

20
MinecraftClient.sln Normal file
View file

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinecraftClient", "MinecraftClient\MinecraftClient.csproj", "{1E2FACE4-F5CA-4323-9641-740C6A551770}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|x86.ActiveCfg = Debug|x86
{1E2FACE4-F5CA-4323-9641-740C6A551770}.Debug|x86.Build.0 = Debug|x86
{1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|x86.ActiveCfg = Release|x86
{1E2FACE4-F5CA-4323-9641-740C6A551770}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

845
MinecraftClient/Bots.cs Normal file
View file

@ -0,0 +1,845 @@
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 namespace "Bots", below.
/// Once your bot is created, simply edit the switch in Program.cs to add the corresponding command-line argument!
///
/// <summary>
/// The virtual class containing anything you need for creating chat bots.
/// </summary>
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
/// <summary>
/// Anything you want to initialize your bot, will be called on load by MinecraftCom
/// </summary>
public virtual void Initialize() { }
/// <summary>
/// Will be called every ~100ms (10fps) if loaded in MinecraftCom
/// </summary>
public virtual void Update() { }
/// <summary>
/// Any text sent by the server will be sent here by MinecraftCom
/// </summary>
/// <param name="text">Text from the server</param>
public virtual void GetText(string text) { }
/// <summary>
/// Is called when the client has been disconnected fom the server
/// </summary>
/// <param name="reason">Disconnect Reason</param>
/// <param name="message">Kick message, if any</param>
/// <returns>Return TRUE if the client is about to restart</returns>
public virtual bool OnDisconnect(DisconnectReason reason, string message) { return false; }
#region ToolBox
/// <summary>
/// Send text to the server. Can be anything such as chat messages or commands
/// </summary>
/// <param name="text">Text to send to the server</param>
protected void SendText(string text)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine("BOT:" + text);
handler.SendChatMessage(text);
Console.ForegroundColor = ConsoleColor.Gray;
}
/// <summary>
/// Remove color codes ("§c") from a text message received from the server
/// </summary>
protected static string getVerbatim(string text)
{
string verbatim = "";
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '§')
{
i++; //Will skip also the next char
}
else verbatim += text[i]; //Add the char
}
return verbatim;
}
/// <summary>
/// Verify that a string contains only a-z A-Z 0-9 and _ characters.
/// </summary>
protected static bool isValidName(string username)
{
if (username == "") { return false; }
string validchars =
"abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "1234567890_";
bool passe = false;
bool ok = true;
for (int i = 0; i < username.Length; i++)
{
passe = false;
for (int j = 0; j < validchars.Length; j++)
{
if (username[i] == validchars[j])
{
passe = true;
break;
}
}
if (!passe)
{
ok = false;
break;
}
}
return ok;
}
/// <summary>
/// Returns true is the text passed is a private message sent to the bot
/// </summary>
/// <param name="text">text to test</param>
/// <param name="message">if it's a private message, this will contain the message</param>
/// <param name="sender">if it's a private message, this will contain the player name that sends the message</param>
/// <returns>Returns true if the text is a private message</returns>
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 message
if (tmp.Length > 2 && tmp[1] == "whispers")
{
message = text.Substring(tmp[0].Length + 10);
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; }
}
/// <summary>
/// Returns true is the text passed is a public message written by a player on the chat
/// </summary>
/// <param name="text">text to test</param>
/// <param name="message">if it's message, this will contain the message</param>
/// <param name="sender">if it's message, this will contain the player name that sends the message</param>
/// <returns>Returns true if the text is a chat message</returns>
protected static bool isChatMessage(string text, ref string message, ref string sender)
{
//Detect chat messages
//<Someone> 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;
}
/// <summary>
/// Writes some text in the console. Nothing will be sent to the server.
/// </summary>
/// <param name="text">Log text to write</param>
public static void LogToConsole(string text)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine("[BOT] " + text);
Console.ForegroundColor = ConsoleColor.Gray;
}
/// <summary>
/// Disconnect from the server and restart the program
/// It will unload & reload all the bots and then reconnect to the server
/// </summary>
protected void ReconnectToTheServer() { ReconnectToTheServer(3); }
/// <summary>
/// Disconnect from the server and restart the program
/// It will unload & reload all the bots and then reconnect to the server
/// </summary>
/// <param name="attempts">If connection fails, the client will make X extra attempts</param>
protected void ReconnectToTheServer(int ExtraAttempts)
{
McTcpClient.AttemptsLeft = ExtraAttempts;
Program.Restart();
}
/// <summary>
/// Unload the chatbot, and release associated memory.
/// </summary>
protected void UnloadBot()
{
handler.BotUnLoad(this);
}
#endregion
}
namespace Bots
{
/// <summary>
/// Example of message receiving.
/// </summary>
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);
}
}
}
/// <summary>
/// This bot sends a /ping command every 60 seconds in order to stay non-afk.
/// </summary>
public class AntiAFK : ChatBot
{
private int count;
private int timeping;
/// <summary>
/// This bot sends a /ping command every X seconds in order to stay non-afk.
/// </summary>
/// <param name="pingparam">Time amount between each ping (10 = 1s, 600 = 1 minute, etc.)</param>
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("/ping");
count = 0;
}
}
}
/// <summary>
/// This bot sends a /list command every X seconds and save the result.
/// </summary>
public class PlayerListLogger : ChatBot
{
private int count;
private int timeping;
private string file;
/// <summary>
/// This bot sends a /list command every X seconds and save the result.
/// </summary>
/// <param name="pingparam">Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.)</param>
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");
}
}
}
/// <summary>
/// "Le jeu du Pendu" (Hangman game)
/// </summary>
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 string[] owners;
private bool English;
/// <summary>
/// "Le jeu du Pendu" (Hangman Game)
/// </summary>
/// <param name="english">if true, the game will be in english. If false, the game will be in french.</param>
public Pendu(bool english)
{
English = english;
}
public override void Initialize()
{
owners = getowners();
}
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 (owners.Contains(username.ToUpper()))
{
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 ? "words.txt" : "mots.txt"))
{
string[] dico = System.IO.File.ReadAllLines(English ? "words.txt" : "mots.txt");
return dico[new Random().Next(dico.Length)];
}
else
{
LogToConsole(English ? "Cannot find words.txt !" : "Fichier mots.txt introuvable !");
return English ? "WORDSAREMISSING" : "DICOMANQUANT";
}
}
private string[] getowners()
{
List<string> owners = new List<string>();
owners.Add("CONSOLE");
if (System.IO.File.Exists("bot-owners.txt"))
{
foreach (string s in System.IO.File.ReadAllLines("bot-owners.txt"))
{
owners.Add(s.ToUpper());
}
}
else LogToConsole(English ? "Cannot find bot-owners.txt !" : "Fichier bot-owners.txt introuvable !");
return owners.ToArray();
}
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;
}
}
}
/// <summary>
/// This bot make the console beep on some specified words. Useful to detect when someone is talking to you, for example.
/// </summary>
public class Alerts : ChatBot
{
private string[] dictionnary = new string[0];
private string[] excludelist = new string[0];
public override void Initialize()
{
if (System.IO.File.Exists("alerts.txt"))
{
dictionnary = System.IO.File.ReadAllLines("alerts.txt");
for (int i = 0; i < dictionnary.Length; i++)
{
dictionnary[i] = dictionnary[i].ToLower();
}
}
else LogToConsole("Cannot find alerts.txt !");
if (System.IO.File.Exists("alerts-exclude.txt"))
{
excludelist = System.IO.File.ReadAllLines("alerts-exclude.txt");
for (int i = 0; i < excludelist.Length; i++)
{
excludelist[i] = excludelist[i].ToLower();
}
}
else LogToConsole("Cannot find alerts-exclude.txt !");
}
public override void GetText(string text)
{
text = getVerbatim(text);
string comp = text.ToLower();
foreach (string alert in dictionnary)
{
if (comp.Contains(alert))
{
bool ok = true;
foreach (string exclusion in excludelist)
{
if (comp.Contains(exclusion))
{
ok = false;
break;
}
}
if (ok)
{
Console.Beep(); //Text found !
#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
}
}
}
}
}
/// <summary>
/// This bot saves the received messages in a text file.
/// </summary>
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;
/// <summary>
/// This bot saves the messages received in the specified file, with some filters and date/time tagging.
/// </summary>
/// <param name="file">The file to save the log in</param>
/// <param name="filter">The kind of messages to save</param>
/// <param name="AddDateAndTime">Add a date and time before each message</param>
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 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();
}
}
/// <summary>
/// This bot automatically re-join the server if kick message contains predefined string (Server is restarting ...)
/// </summary>
public class AutoRelog : ChatBot
{
private string[] dictionnary = new string[0];
private int attempts;
private int delay;
/// <summary>
/// This bot automatically re-join the server if kick message contains predefined string
/// </summary>
/// <param name="DelayBeforeRelog">Delay before re-joining the server (in seconds)</param>
/// <param name="retries">Number of retries if connection fails (-1 = infinite)</param>
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("kickmessages.txt"))
{
dictionnary = System.IO.File.ReadAllLines("kickmessages.txt");
for (int i = 0; i < dictionnary.Length; i++)
{
dictionnary[i] = dictionnary[i].ToLower();
}
}
else LogToConsole("Cannot find kickmessages.txt !");
}
public override bool OnDisconnect(DisconnectReason reason, string message)
{
message = getVerbatim(message);
string comp = message.ToLower();
foreach (string msg in dictionnary)
{
if (comp.Contains(msg))
{
LogToConsole("Waiting " + delay + " seconds before reconnecting...");
System.Threading.Thread.Sleep(delay * 1000);
McTcpClient.AttemptsLeft = attempts;
ReconnectToTheServer();
return true;
}
}
return false;
}
}
/// <summary>
/// Automatically send login command on servers usign the xAuth plugin
/// </summary>
public class xAuth : ChatBot
{
private string password;
private int countdown = 50;
public xAuth(string pass)
{
password = pass;
}
public override void Update()
{
countdown--;
if (countdown == 0)
{
SendText("/login " + password);
UnloadBot(); //This bot is no more needed.
}
}
}
}
}

Binary file not shown.

View file

@ -0,0 +1,297 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MinecraftClient
{
/// <summary>
/// This class parses JSON chat data from MC 1.6+ and returns the appropriate string to be printed.
/// </summary>
static class ChatParser
{
/// <summary>
/// The main function to convert text from MC 1.6+ JSON to MC 1.5.2 formatted text
/// </summary>
/// <param name="json">JSON serialized text</param>
/// <returns>Returns the translated text</returns>
public static string ParseText(string json)
{
int cursorpos = 0;
JSONData jsonData = String2Data(json, ref cursorpos);
return JSONData2String(jsonData).Replace("u0027", "'");
}
/// <summary>
/// An internal class to store unserialized JSON data
/// The data can be an object, an array or a string
/// </summary>
private class JSONData
{
public enum DataType { Object, Array, String };
private DataType type;
public DataType Type { get { return type; } }
public Dictionary<string, JSONData> Properties;
public List<JSONData> DataArray;
public string StringValue;
public JSONData(DataType datatype)
{
type = datatype;
Properties = new Dictionary<string, JSONData>();
DataArray = new List<JSONData>();
StringValue = String.Empty;
}
}
/// <summary>
/// Get the classic color tag corresponding to a color name
/// </summary>
/// <param name="colorname">Color Name</param>
/// <returns>Color code</returns>
private static string color2tag(string colorname)
{
switch(colorname.ToLower())
{
case "black": return "§0";
case "dark_blue": return "§1";
case "dark_green" : return "§2";
case "dark_cyan": return "§3";
case "dark_cyanred": return "§4";
case "dark_magenta": return "§5";
case "dark_yellow": return "§6";
case "gray": return "§7";
case "dark_gray": return "§8";
case "blue": return "§9";
case "green": return "§a";
case "cyan": return "§b";
case "red": return "§c";
case "magenta": return "§d";
case "yellow": return "§e";
case "white": return "§f";
default: return "";
}
}
/// <summary>
/// Rules for text translation
/// </summary>
private static bool init = false;
private static Dictionary<string, string> TranslationRules = new Dictionary<string, string>();
public static void InitTranslations() { if (!init) { InitRules(); init = true; } }
private static void InitRules()
{
//Small default dictionnary of translation rules
TranslationRules["chat.type.admin"] = "[%s: %s]";
TranslationRules["chat.type.announcement"] = "§d[%s] %s";
TranslationRules["chat.type.emote"] = " * %s %s";
TranslationRules["chat.type.text"] = "<%s> %s";
TranslationRules["multiplayer.player.joined"] = "§e%s joined the game.";
TranslationRules["multiplayer.player.left"] = "§e%s left the game.";
TranslationRules["commands.message.display.incoming"] = "§7%s whispers to you: %s";
TranslationRules["commands.message.display.outgoing"] = "§7You whisper to %s: %s";
//Load an external dictionnary of translation rules
if (System.IO.File.Exists("translations.lang"))
{
string[] translations = System.IO.File.ReadAllLines("translations.lang");
foreach (string line in translations)
{
if (line.Length > 0)
{
string[] splitted = line.Split('=');
if (splitted.Length == 2)
{
TranslationRules[splitted[0]] = splitted[1];
}
}
}
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine("Translations file loaded.");
Console.ForegroundColor = ConsoleColor.Gray;
}
else //No external dictionnary found.
{
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine("MC 1.6+ warning: Translations file \"translations.lang\" not found."
+ "\nYou can pick a translation file from .minecraft\\assets\\lang\\"
+ "\nCopy to the same folder as MinecraftClient & rename to \"translations.lang\""
+ "\nSome messages won't be properly printed without this file.");
Console.ForegroundColor = ConsoleColor.Gray;
}
}
/// <summary>
/// Format text using a specific formatting rule.
/// Example : * %s %s + ["ORelio", "is doing something"] = * ORelio is doing something
/// </summary>
/// <param name="rulename">Name of the rule, chosen by the server</param>
/// <param name="using_data">Data to be used in the rule</param>
/// <returns>Returns the formatted text according to the given data</returns>
private static string TranslateString(string rulename, List<string> using_data)
{
if (!init) { InitRules(); init = true; }
if (TranslationRules.ContainsKey(rulename))
{
string[] syntax = TranslationRules[rulename].Split(new string[2] { "%s", "%d" }, StringSplitOptions.None);
while (using_data.Count < syntax.Length - 1) { using_data.Add(""); }
string[] using_array = using_data.ToArray();
string translated = "";
for (int i = 0; i < syntax.Length - 1; i++)
{
translated += syntax[i];
translated += using_array[i];
}
translated += syntax[syntax.Length - 1];
return translated;
}
else return "[" + rulename + "] " + String.Join(" ", using_data);
}
/// <summary>
/// Parse a JSON string to build a JSON object
/// </summary>
/// <param name="toparse">String to parse</param>
/// <param name="cursorpos">Cursor start (set to 0 for function init)</param>
/// <returns></returns>
private static JSONData String2Data(string toparse, ref int cursorpos)
{
try
{
JSONData data;
switch (toparse[cursorpos])
{
//Object
case '{':
data = new JSONData(JSONData.DataType.Object);
cursorpos++;
while (toparse[cursorpos] != '}')
{
if (toparse[cursorpos] == '"')
{
JSONData propertyname = String2Data(toparse, ref cursorpos);
if (toparse[cursorpos] == ':') { cursorpos++; } else { /* parse error ? */ }
JSONData propertyData = String2Data(toparse, ref cursorpos);
data.Properties[propertyname.StringValue] = propertyData;
}
else cursorpos++;
}
cursorpos++;
break;
//Array
case '[':
data = new JSONData(JSONData.DataType.Array);
cursorpos++;
while (toparse[cursorpos] != ']')
{
if (toparse[cursorpos] == ',') { cursorpos++; }
JSONData arrayItem = String2Data(toparse, ref cursorpos);
data.DataArray.Add(arrayItem);
}
cursorpos++;
break;
//String
case '"':
data = new JSONData(JSONData.DataType.String);
cursorpos++;
while (toparse[cursorpos] != '"')
{
if (toparse[cursorpos] == '\\') { cursorpos++; }
data.StringValue += toparse[cursorpos];
cursorpos++;
}
cursorpos++;
break;
//Boolean : true
case 't':
data = new JSONData(JSONData.DataType.String);
cursorpos++;
if (toparse[cursorpos] == 'r') { cursorpos++; }
if (toparse[cursorpos] == 'u') { cursorpos++; }
if (toparse[cursorpos] == 'e') { cursorpos++; data.StringValue = "true"; }
break;
//Boolean : false
case 'f':
data = new JSONData(JSONData.DataType.String);
cursorpos++;
if (toparse[cursorpos] == 'a') { cursorpos++; }
if (toparse[cursorpos] == 'l') { cursorpos++; }
if (toparse[cursorpos] == 's') { cursorpos++; }
if (toparse[cursorpos] == 'e') { cursorpos++; data.StringValue = "false"; }
break;
//Unknown data
default:
cursorpos++;
return String2Data(toparse, ref cursorpos);
}
return data;
}
catch (IndexOutOfRangeException)
{
return new JSONData(JSONData.DataType.String);
}
}
/// <summary>
/// Use a JSON Object to build the corresponding string
/// </summary>
/// <param name="data">JSON object to convert</param>
/// <returns>returns the Minecraft-formatted string</returns>
private static string JSONData2String(JSONData data)
{
string colorcode = "";
switch (data.Type)
{
case JSONData.DataType.Object:
if (data.Properties.ContainsKey("color"))
{
colorcode = color2tag(JSONData2String(data.Properties["color"]));
}
if (data.Properties.ContainsKey("text"))
{
return colorcode + JSONData2String(data.Properties["text"]) + colorcode;
}
else if (data.Properties.ContainsKey("translate"))
{
List<string> using_data = new List<string>();
if (data.Properties.ContainsKey("using"))
{
JSONData[] array = data.Properties["using"].DataArray.ToArray();
for (int i = 0; i < array.Length; i++)
{
using_data.Add(JSONData2String(array[i]));
}
}
return colorcode + TranslateString(JSONData2String(data.Properties["translate"]), using_data) + colorcode;
}
else return "";
case JSONData.DataType.Array:
string result = "";
foreach (JSONData item in data.DataArray)
{
result += JSONData2String(item);
}
return result;
case JSONData.DataType.String:
return data.StringValue;
}
return "";
}
}
}

View file

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MinecraftClient
{
/// <summary>
/// Allows simultaneous console input and output without breaking user input
/// (Without having this annoying behaviour : User inp[Some Console output]ut)
/// </summary>
public static class ConsoleIO
{
public static void Reset() { if (reading) { reading = false; Console.Write("\b \b"); } }
private static LinkedList<string> previous = new LinkedList<string>();
private static string buffer = "";
private static string buffer2 = "";
private static bool consolelock = false;
private static bool reading = false;
private static bool writing = false;
#region Read User Input
public static string ReadLine()
{
ConsoleKeyInfo k = new ConsoleKeyInfo();
Console.Write('>');
reading = true;
buffer = "";
buffer2 = "";
while (k.Key != ConsoleKey.Enter)
{
k = Console.ReadKey(true);
while (writing) { }
consolelock = true;
switch (k.Key)
{
case ConsoleKey.Escape:
ClearLineAndBuffer();
break;
case ConsoleKey.Backspace:
RemoveOneChar();
break;
case ConsoleKey.Enter:
Console.Write('\n');
break;
case ConsoleKey.LeftArrow:
GoLeft();
break;
case ConsoleKey.RightArrow:
GoRight();
break;
case ConsoleKey.Home:
while (buffer.Length > 0) { GoLeft(); }
break;
case ConsoleKey.End:
while (buffer2.Length > 0) { GoRight(); }
break;
case ConsoleKey.Delete:
if (buffer2.Length > 0)
{
GoRight();
RemoveOneChar();
}
break;
case ConsoleKey.Oem6:
break;
case ConsoleKey.DownArrow:
if (previous.Count > 0)
{
ClearLineAndBuffer();
buffer = previous.First.Value;
previous.AddLast(buffer);
previous.RemoveFirst();
Console.Write(buffer);
}
break;
case ConsoleKey.UpArrow:
if (previous.Count > 0)
{
ClearLineAndBuffer();
buffer = previous.Last.Value;
previous.AddFirst(buffer);
previous.RemoveLast();
Console.Write(buffer);
}
break;
default:
AddChar(k.KeyChar);
break;
}
consolelock = false;
}
while (writing) { }
reading = false;
previous.AddLast(buffer + buffer2);
return buffer + buffer2;
}
#endregion
#region Console Output
public static void Write(string text)
{
while (consolelock) { }
writing = true;
if (reading)
{
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
string buf = buffer;
string buf2 = buffer2;
ClearLineAndBuffer();
if (Console.CursorLeft == 0)
{
Console.CursorLeft = Console.BufferWidth - 1;
Console.CursorTop--;
Console.Write(' ');
Console.CursorLeft = Console.BufferWidth - 1;
Console.CursorTop--;
}
else Console.Write("\b \b");
Console.Write(text);
Console.ForegroundColor = ConsoleColor.Gray;
Console.BackgroundColor = ConsoleColor.Black;
buffer = buf;
buffer2 = buf2;
Console.Write(">" + buffer);
if (buffer2.Length > 0)
{
Console.Write(buffer2 + " \b");
for (int i = 0; i < buffer2.Length; i++) { GoBack(); }
}
Console.ForegroundColor = fore;
Console.BackgroundColor = back;
}
else Console.Write(text);
writing = false;
}
public static void WriteLine(string line)
{
Write(line + '\n');
}
public static void Write(char c)
{
Write("" + c);
}
#endregion
#region subfunctions
private static void ClearLineAndBuffer()
{
while (buffer2.Length > 0) { GoRight(); }
while (buffer.Length > 0) { RemoveOneChar(); }
}
private static void RemoveOneChar()
{
if (buffer.Length > 0)
{
if (Console.CursorLeft == 0)
{
Console.CursorLeft = Console.BufferWidth - 1;
Console.CursorTop--;
Console.Write(' ');
Console.CursorLeft = Console.BufferWidth - 1;
Console.CursorTop--;
}
else Console.Write("\b \b");
buffer = buffer.Substring(0, buffer.Length - 1);
if (buffer2.Length > 0)
{
Console.Write(buffer2 + " \b");
for (int i = 0; i < buffer2.Length; i++) { GoBack(); }
}
}
}
private static void GoBack()
{
if (buffer.Length > 0)
{
if (Console.CursorLeft == 0)
{
Console.CursorLeft = Console.BufferWidth - 1;
Console.CursorTop--;
}
else Console.Write('\b');
}
}
private static void GoLeft()
{
if (buffer.Length > 0)
{
buffer2 = "" + buffer[buffer.Length - 1] + buffer2;
buffer = buffer.Substring(0, buffer.Length - 1);
Console.Write('\b');
}
}
private static void GoRight()
{
if (buffer2.Length > 0)
{
buffer = buffer + buffer2[0];
Console.Write(buffer2[0]);
buffer2 = buffer2.Substring(1);
}
}
private static void AddChar(char c)
{
Console.Write(c);
buffer += c;
Console.Write(buffer2);
for (int i = 0; i < buffer2.Length; i++) { GoBack(); }
}
#endregion
}
}

203
MinecraftClient/Crypto.cs Normal file
View file

@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using java.security;
using java.security.spec;
using javax.crypto;
using javax.crypto.spec;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
namespace MinecraftClient
{
/// <summary>
/// Cryptographic functions ported from Minecraft Source Code (Java). Decompiled with MCP. Copy, paste, little adjustements.
/// </summary>
public class Crypto
{
public static PublicKey GenerateRSAPublicKey(byte[] key)
{
X509EncodedKeySpec localX509EncodedKeySpec = new X509EncodedKeySpec(key);
KeyFactory localKeyFactory = KeyFactory.getInstance("RSA");
return localKeyFactory.generatePublic(localX509EncodedKeySpec);
}
public static SecretKey GenerateAESPrivateKey()
{
CipherKeyGenerator var0 = new CipherKeyGenerator();
var0.Init(new KeyGenerationParameters(new Org.BouncyCastle.Security.SecureRandom(), 128));
return new SecretKeySpec(var0.GenerateKey(), "AES");
}
public static byte[] getServerHash(String toencode, PublicKey par1PublicKey, SecretKey par2SecretKey)
{
return digest("SHA-1", new byte[][] { Encoding.GetEncoding("iso-8859-1").GetBytes(toencode), par2SecretKey.getEncoded(), par1PublicKey.getEncoded() });
}
public static byte[] Encrypt(Key par0Key, byte[] par1ArrayOfByte)
{
return func_75885_a(1, par0Key, par1ArrayOfByte);
}
private static byte[] digest(String par0Str, byte[][] par1ArrayOfByte)
{
MessageDigest var2 = MessageDigest.getInstance(par0Str);
byte[][] var3 = par1ArrayOfByte;
int var4 = par1ArrayOfByte.Length;
for (int var5 = 0; var5 < var4; ++var5)
{
byte[] var6 = var3[var5];
var2.update(var6);
}
return var2.digest();
}
private static byte[] func_75885_a(int par0, Key par1Key, byte[] par2ArrayOfByte)
{
try
{
return cypherencrypt(par0, par1Key.getAlgorithm(), par1Key).doFinal(par2ArrayOfByte);
}
catch (IllegalBlockSizeException var4)
{
var4.printStackTrace();
}
catch (BadPaddingException var5)
{
var5.printStackTrace();
}
Console.Error.WriteLine("Cipher data failed!");
return null;
}
private static Cipher cypherencrypt(int par0, String par1Str, Key par2Key)
{
try
{
Cipher var3 = Cipher.getInstance(par1Str);
var3.init(par0, par2Key);
return var3;
}
catch (InvalidKeyException var4)
{
var4.printStackTrace();
}
catch (NoSuchAlgorithmException var5)
{
var5.printStackTrace();
}
catch (NoSuchPaddingException var6)
{
var6.printStackTrace();
}
Console.Error.WriteLine("Cipher creation failed!");
return null;
}
public static AesStream SwitchToAesMode(System.IO.Stream stream, Key key)
{
return new AesStream(stream, key.getEncoded());
}
/// <summary>
/// An encrypted stream using AES
/// </summary>
public class AesStream : System.IO.Stream
{
CryptoStream enc;
CryptoStream dec;
public AesStream(System.IO.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;
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,243 @@
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;
namespace MinecraftClient
{
/// <summary>
/// The main client class, used to connect to a Minecraft server.
/// It allows message sending and text receiving.
/// </summary>
class McTcpClient
{
public static int AttemptsLeft = 0;
string host;
int port;
string username;
string text;
Thread t_updater;
Thread t_sender;
TcpClient client;
MinecraftCom handler;
/// <summary>
/// Starts the main chat client, wich will login to the server using the MinecraftCom class.
/// </summary>
/// <param name="username">The chosen username of a premium Minecraft Account</param>
/// <param name="sessionID">A valid sessionID obtained with MinecraftCom.GetLogin()</param>
/// <param name="server_port">The server IP (serveradress or serveradress:port)</param>
public McTcpClient(string username, string sessionID, string server_port, MinecraftCom handler)
{
StartClient(username, sessionID, server_port, false, handler, "");
}
/// <summary>
/// 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.
/// </summary>
/// <param name="username">The chosen username of a premium Minecraft Account</param>
/// <param name="sessionID">A valid sessionID obtained with MinecraftCom.GetLogin()</param>
/// <param name="server_port">The server IP (serveradress or serveradress:port)</param>
/// <param name="command">The text or command to send.</param>
public McTcpClient(string username, string sessionID, string server_port, MinecraftCom handler, string command)
{
StartClient(username, sessionID, server_port, true, handler, command);
}
/// <summary>
/// Starts the main chat client, wich will login to the server using the MinecraftCom class.
/// </summary>
/// <param name="user">The chosen username of a premium Minecraft Account</param>
/// <param name="sessionID">A valid sessionID obtained with MinecraftCom.GetLogin()</param>
/// <param name="server_port">The server IP (serveradress or serveradress:port)/param>
/// <param name="singlecommand">If set to true, the client will send a single command and then disconnect from the server</param>
/// <param name="command">The text or command to send. Will only be sent if singlecommand is set to true.</param>
private void StartClient(string user, string sessionID, string server_port, bool singlecommand, MinecraftCom handler, string command)
{
this.handler = handler;
username = user;
string[] sip = server_port.Split(':');
host = sip[0];
if (sip.Length == 1)
{
port = 25565;
}
else
{
try
{
port = Convert.ToInt32(sip[1]);
}
catch (FormatException) { port = 25565; }
}
try
{
Console.WriteLine("Connecting...");
client = new TcpClient(host, port);
client.ReceiveBufferSize = 1024 * 1024;
handler.setClient(client);
byte[] token = new byte[1]; string serverID = "";
if (handler.Handshake(user, sessionID, ref serverID, ref token, host, port))
{
Console.WriteLine("Logging in...");
if (handler.FinalizeLogin())
{
//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.");
Thread.Sleep(5000);
handler.Disconnect("disconnect.quitting");
Thread.Sleep(1000);
}
else
{
Console.WriteLine("Server was successfuly 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();
}
}
else
{
Console.WriteLine("Login failed.");
if (!singlecommand) { Program.ReadLineReconnect(); }
}
}
else
{
Console.WriteLine("Invalid session ID.");
if (!singlecommand) { Program.ReadLineReconnect(); }
}
}
catch (SocketException)
{
Console.WriteLine("Failed to connect to this IP.");
if (AttemptsLeft > 0)
{
ChatBot.LogToConsole("Waiting 5 seconds (" + AttemptsLeft + " attempts left)...");
Thread.Sleep(5000); AttemptsLeft--; Program.Restart();
}
else if (!singlecommand){ Console.ReadLine(); }
}
}
/// <summary>
/// Allows the user to send chat messages, commands, and to leave the server.
/// Will be automatically called on a separate Thread by StartClient()
/// </summary>
private void StartTalk()
{
try
{
while (client.Client.Connected)
{
text = ConsoleIO.ReadLine();
if (text == "/quit" || text == "/reco" || text == "/reconnect") { break; }
while (text.Length > 0 && text[0] == ' ') { text = text.Substring(1); }
if (text != "")
{
//Message is too long
if (text.Length > 100)
{
if (text[0] == '/')
{
//Send the first 100 chars of the command
text = text.Substring(0, 100);
handler.SendChatMessage(text);
}
else
{
//Send the message splitted in sereval messages
while (text.Length > 100)
{
handler.SendChatMessage(text.Substring(0, 100));
text = text.Substring(100, text.Length - 100);
}
handler.SendChatMessage(text);
}
}
else handler.SendChatMessage(text);
}
}
if (text == "/quit")
{
ConsoleIO.WriteLine("You have left the server.");
Disconnect();
}
else if (text == "/reco" || text == "/reconnect")
{
ConsoleIO.WriteLine("You have left the server.");
Program.Restart();
}
}
catch (IOException) { }
}
/// <summary>
/// Receive the data (including chat messages) from the server, and keep the connection alive.
/// Will be automatically called on a separate Thread by StartClient()
/// </summary>
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() && !Program.ReadLineReconnect()) { t_sender.Abort(); }
}
else if (Program.ReadLineReconnect()) { t_sender.Abort(); }
}
/// <summary>
/// Disconnect the client from the server
/// </summary>
public void Disconnect()
{
handler.Disconnect("disconnect.quitting");
Thread.Sleep(1000);
if (t_updater != null) { t_updater.Abort(); }
if (t_sender != null) { t_sender.Abort(); }
if (client != null) { client.Close(); }
}
}
}

View file

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{1E2FACE4-F5CA-4323-9641-740C6A551770}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MinecraftClient</RootNamespace>
<AssemblyName>MinecraftClient</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup />
<PropertyGroup>
<SignManifests>false</SignManifests>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4114.6375, Culture=neutral, PublicKeyToken=0e99375e54769942">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\BouncyCastle.Crypto.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.Core, Version=7.0.4335.0, Culture=neutral, PublicKeyToken=13235d27fcbfff58, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\IKVM.OpenJDK.Core.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.Security, Version=7.0.4335.0, Culture=neutral, PublicKeyToken=13235d27fcbfff58, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\IKVM.OpenJDK.Security.dll</HintPath>
</Reference>
<Reference Include="IKVM.OpenJDK.Util, Version=7.0.4335.0, Culture=neutral, PublicKeyToken=13235d27fcbfff58, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\IKVM.OpenJDK.Util.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Bots.cs" />
<Compile Include="ConsoleIO.cs" />
<Compile Include="Crypto.cs" />
<Compile Include="ChatParser.cs" />
<Compile Include="MinecraftCom.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="McTcpClient.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Content Include="BouncyCastle.Crypto.dll" />
<Content Include="IKVM.OpenJDK.Core.dll" />
<Content Include="IKVM.OpenJDK.Security.dll" />
<Content Include="IKVM.OpenJDK.Util.dll" />
<Content Include="IKVM.Runtime.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<StartArguments>
</StartArguments>
</PropertyGroup>
<PropertyGroup>
<PublishUrlHistory>publish\</PublishUrlHistory>
<InstallUrlHistory />
<SupportUrlHistory />
<UpdateUrlHistory />
<BootstrapperUrlHistory />
<ErrorReportUrlHistory />
<FallbackCulture>en-US</FallbackCulture>
<VerifyUploadedFiles>false</VerifyUploadedFiles>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<StartArguments>
</StartArguments>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,825 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace MinecraftClient
{
/// <summary>
/// The class containing all the core functions needed to communicate with a Minecraft server.
/// </summary>
public class MinecraftCom
{
#region Login to Minecraft.net, Obtaining a session ID
public enum LoginResult { Error, Success, WrongPassword, Blocked, AccountMigrated, NotPremium };
/// <summary>
/// Allows to login to a premium Minecraft account, and retrieve the session ID.
/// </summary>
/// <param name="user">Login</param>
/// <param name="pass">Password</param>
/// <param name="outdata">Will contain the data returned by Minecraft.net, if the login is successful : Version:UpdateTicket:Username:SessionID</param>
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
public static LoginResult GetLogin(string user, string pass, ref string outdata)
{
try
{
Console.ForegroundColor = ConsoleColor.DarkGray;
WebClient wClient = new WebClient();
Console.WriteLine("https://login.minecraft.net/?user=" + user + "&password=<******>&version=13");
string result = wClient.DownloadString("https://login.minecraft.net/?user=" + user + "&password=" + pass + "&version=13");
outdata = result;
Console.WriteLine(result);
Console.ForegroundColor = ConsoleColor.Gray;
if (result == "Bad login") { return LoginResult.WrongPassword; }
if (result == "User not premium") { return LoginResult.NotPremium; }
if (result == "Too many failed logins") { return LoginResult.Blocked; }
if (result == "Account migrated, use e-mail as username.") { return LoginResult.AccountMigrated; }
else return LoginResult.Success;
}
catch (WebException) { return LoginResult.Error; }
}
#endregion
#region Keep-Alive for a Minecraft.net session, should be called every 5 minutes (currently unused)
/// <summary>
/// The session ID will expire within 5 minutes unless this function is called every 5 minutes
/// </summary>
/// <param name="user">Username</param>
/// <param name="sessionID">Session ID to keep alive</param>
public static void SessionKeepAlive(string user, string sessionID)
{
new WebClient().DownloadString("https://login.minecraft.net/session?name=" + user + "&session=" + sessionID);
}
#endregion
#region Session checking when joining a server in online mode
/// <summary>
/// This method allows to join an online-mode server.
/// It Should be called between the handshake and the login attempt.
/// </summary>
/// <param name="user">Username</param>
/// <param name="sessionID">A valid session ID for this username</param>
/// <param name="hash">Hash returned by the server during the handshake</param>
/// <returns>Returns true if the check was successful</returns>
public static bool SessionCheck(string user, string sessionID, string hash)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
WebClient client = new WebClient();
Console.Write("http://session.minecraft.net/game/joinserver.jsp?user=" + user + "&sessionId=" + sessionID + "&serverId=" + hash + " ... ");
string result = client.DownloadString("http://session.minecraft.net/game/joinserver.jsp?user=" + user + "&sessionId=" + sessionID + "&serverId=" + hash);
Console.WriteLine(result);
Console.ForegroundColor = ConsoleColor.Gray;
return (result == "OK");
}
#endregion
#region Server-side session checking (programmed for testing purposes)
/// <summary>
/// Reproduces the username checking done by an online-mode server during the login process.
/// </summary>
/// <param name="user">Username</param>
/// <param name="hash">Hash sent by the server during the handshake</param>
/// <returns>Returns true if the user is allowed to join the server</returns>
public static bool ServerSessionCheck(string user, string hash)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
WebClient client = new WebClient();
ConsoleIO.WriteLine("http://session.minecraft.net/game/checkserver.jsp?user=" + user + "&serverId=" + hash);
string result = client.DownloadString("http://session.minecraft.net/game/checkserver.jsp?user=" + user + "&serverId=" + hash);
ConsoleIO.WriteLine(result);
Console.ForegroundColor = ConsoleColor.Gray;
return (result == "YES");
}
#endregion
TcpClient c = new TcpClient();
Crypto.AesStream s;
public bool HasBeenKicked { get { return connectionlost; } }
bool connectionlost = false;
bool encrypted = false;
byte protocolversion;
public bool Update()
{
for (int i = 0; i < bots.Count; i++) { bots[i].Update(); }
if (c.Client == null || !c.Connected) { return false; }
byte id = 0;
try
{
while (c.Client.Available > 0)
{
id = readNextByte();
ProcessResult result = processPacket(id);
//Debug : Print packet IDs that are beign processed. Green = OK, Red = Unknown packet
//If the client gets out of sync, check the last green packet processing code.
//if (result == ProcessResult.OK) { printstring("§a0x" + id.ToString("X"), false); }
//else { printstring("§c0x" + id.ToString("X"), false); }
if (result == ProcessResult.ConnectionLost)
{
return false;
}
}
}
catch (SocketException) { return false; }
return true;
}
public void DebugDump()
{
byte[] cache = new byte[128000];
Receive(cache, 0, 128000, SocketFlags.None);
string dump = BitConverter.ToString(cache);
System.IO.File.WriteAllText("debug.txt", dump);
System.Diagnostics.Process.Start("debug.txt");
}
public bool OnConnectionLost()
{
if (!connectionlost)
{
connectionlost = true;
for (int i = 0; i < bots.Count; i++)
{
if (bots[i].OnDisconnect(ChatBot.DisconnectReason.ConnectionLost, "Connection has been lost."))
{
return true; //The client is about to restart
}
}
}
return false;
}
private enum ProcessResult { OK, ConnectionLost, UnknownPacket }
private ProcessResult processPacket(int id)
{
int nbr = 0;
switch (id)
{
case 0x00: byte[] keepalive = new byte[5] { 0, 0, 0, 0, 0 };
Receive(keepalive, 1, 4, SocketFlags.None);
Send(keepalive); break;
case 0x01: readData(4); readNextString(); readData(5); break;
case 0x02: readData(1); readNextString(); readNextString(); readData(4); break;
case 0x03:
string message = readNextString();
if (protocolversion >= 72)
{
//printstring("§8" + message, false); //Debug
message = ChatParser.ParseText(message);
printstring(message, false);
}
else printstring(message, false);
for (int i = 0; i < bots.Count; i++) { bots[i].GetText(message); } break;
case 0x04: readData(16); break;
case 0x05: readData(6); readNextItemSlot(); break;
case 0x06: readData(12); break;
case 0x07: readData(9); break;
case 0x08: if (protocolversion >= 72) { readData(10); } else readData(8); break;
case 0x09: readData(8); readNextString(); break;
case 0x0A: readData(1); break;
case 0x0B: readData(33); break;
case 0x0C: readData(9); break;
case 0x0D: readData(41); break;
case 0x0E: readData(11); break;
case 0x0F: readData(10); readNextItemSlot(); readData(3); break;
case 0x10: readData(2); break;
case 0x11: readData(14); break;
case 0x12: readData(5); break;
case 0x13: if (protocolversion >= 72) { readData(9); } else readData(5); break;
case 0x14: readData(4); readNextString(); readData(16); readNextEntityMetaData(); break;
case 0x16: readData(8); break;
case 0x17: readData(19); readNextObjectData(); break;
case 0x18: readData(26); readNextEntityMetaData(); break;
case 0x19: readData(4); readNextString(); readData(16); break;
case 0x1A: readData(18); break;
case 0x1B: if (protocolversion >= 72) { readData(10); } break;
case 0x1C: readData(10); break;
case 0x1D: nbr = (int)readNextByte(); readData(nbr * 4); break;
case 0x1E: readData(4); break;
case 0x1F: readData(7); break;
case 0x20: readData(6); break;
case 0x21: readData(9); break;
case 0x22: readData(18); break;
case 0x23: readData(5); break;
case 0x26: readData(5); break;
case 0x27: if (protocolversion >= 72) { readData(9); } else readData(8); break;
case 0x28: readData(4); readNextEntityMetaData(); break;
case 0x29: readData(8); break;
case 0x2A: readData(5); break;
case 0x2B: readData(8); break;
case 0x2C: if (protocolversion >= 72) { readNextEntityProperties(protocolversion); } break;
case 0x33: readData(13); nbr = readNextInt(); readData(nbr); break;
case 0x34: readData(10); nbr = readNextInt(); readData(nbr); break;
case 0x35: readData(12); break;
case 0x36: readData(14); break;
case 0x37: readData(17); break;
case 0x38: readNextChunkBulkData(); break;
case 0x3C: readData(28); nbr = readNextInt(); readData(3 * nbr); readData(12); break;
case 0x3D: readData(18); break;
case 0x3E: readNextString(); readData(17); break;
case 0x3F: if (protocolversion > 51) { readNextString(); readData(32); } break;
case 0x46: readData(2); break;
case 0x47: readData(17); break;
case 0x64: readNextWindowData(protocolversion); break;
case 0x65: readData(1); break;
case 0x66: readData(7); readNextItemSlot(); break;
case 0x67: readData(3); readNextItemSlot(); break;
case 0x68: readData(1); for (nbr = readNextShort(); nbr > 0; nbr--) { readNextItemSlot(); } break;
case 0x69: readData(5); break;
case 0x6A: readData(4); break;
case 0x6B: readData(2); readNextItemSlot(); break;
case 0x6C: readData(2); break;
case 0x82: readData(10); readNextString(); readNextString(); readNextString(); readNextString(); break;
case 0x83: readData(4); nbr = readNextShort(); readData(nbr); break;
case 0x84: readData(11); nbr = readNextShort(); if (nbr > 0) { readData(nbr); } break;
case 0x85: if (protocolversion >= 74) { readData(13); } break;
case 0xC8: if (protocolversion >= 72) { readData(8); } else readData(5); break;
case 0xC9: readNextString(); readData(3); break;
case 0xCA: if (protocolversion >= 72) { readData(9); } else readData(3); break;
case 0xCB: readNextString(); break;
case 0xCC: readNextString(); readData(4); break;
case 0xCD: readData(1); break;
case 0xCE: if (protocolversion > 51) { readNextString(); readNextString(); readData(1); } break;
case 0xCF: if (protocolversion > 51) { readNextString(); readData(1); readNextString(); } readData(4); break;
case 0xD0: if (protocolversion > 51) { readData(1); readNextString(); } break;
case 0xD1: if (protocolversion > 51) { readNextTeamData(); } break;
case 0xFA: readNextString(); nbr = readNextShort(); readData(nbr); break;
case 0xFF: string reason = readNextString();
ConsoleIO.WriteLine("Disconnected by Server :"); printstring(reason, true); connectionlost = true;
for (int i = 0; i < bots.Count; i++) { bots[i].OnDisconnect(ChatBot.DisconnectReason.InGameKick, reason); } return ProcessResult.ConnectionLost;
default: return ProcessResult.UnknownPacket; //unknown packet!
}
return ProcessResult.OK; //packet has been successfully skipped
}
private void readData(int offset)
{
if (offset > 0)
{
try
{
byte[] cache = new byte[offset];
Receive(cache, 0, offset, SocketFlags.None);
}
catch (OutOfMemoryException) { }
}
}
private string readNextString()
{
short lenght = readNextShort();
if (lenght > 0)
{
byte[] cache = new byte[lenght * 2];
Receive(cache, 0, lenght * 2, SocketFlags.None);
string result = ByteArrayToString(cache);
return result;
}
else return "";
}
private byte[] readNextByteArray()
{
short len = readNextShort();
byte[] data = new byte[len];
Receive(data, 0, len, SocketFlags.None);
return data;
}
private short readNextShort()
{
byte[] tmp = new byte[2];
Receive(tmp, 0, 2, SocketFlags.None);
Array.Reverse(tmp);
return BitConverter.ToInt16(tmp, 0);
}
private int readNextInt()
{
byte[] tmp = new byte[4];
Receive(tmp, 0, 4, SocketFlags.None);
Array.Reverse(tmp);
return BitConverter.ToInt32(tmp, 0);
}
private byte readNextByte()
{
byte[] result = new byte[1];
Receive(result, 0, 1, SocketFlags.None);
return result[0];
}
private void readNextItemSlot()
{
short itemid = readNextShort();
//If slot not empty (item ID != -1)
if (itemid != -1)
{
readData(1); //Item count
readData(2); //Item damage
short length = readNextShort();
//If lenght of optional NBT data > 0, read it
if (length > 0) { readData(length); }
}
}
private void readNextEntityMetaData()
{
do
{
byte[] id = new byte[1];
Receive(id, 0, 1, SocketFlags.None);
if (id[0] == 0x7F) { break; }
int index = id[0] & 0x1F;
int type = id[0] >> 5;
switch (type)
{
case 0: readData(1); break; //Byte
case 1: readData(2); break; //Short
case 2: readData(4); break; //Int
case 3: readData(4); break; //Float
case 4: readNextString(); break; //String
case 5: readNextItemSlot(); break; //Slot
case 6: readData(12); break; //Vector (3 Int)
}
} while (true);
}
private void readNextObjectData()
{
int id = readNextInt();
if (id != 0) { readData(6); }
}
private void readNextTeamData()
{
readNextString(); //Internal Name
byte mode = readNextByte();
if (mode == 0 || mode == 2)
{
readNextString(); //Display Name
readNextString(); //Prefix
readNextString(); //Suffix
readData(1); //Friendly Fire
}
if (mode == 0 || mode == 3 || mode == 4)
{
short count = readNextShort();
for (int i = 0; i < count; i++)
{
readNextString(); //Players
}
}
}
private void readNextEntityProperties(int protocolversion)
{
if (protocolversion >= 72)
{
if (protocolversion >= 74)
{
//Minecraft 1.6.2
readNextInt(); //Entity ID
int count = readNextInt();
for (int i = 0; i < count; i++)
{
readNextString(); //Property name
readData(8); //Property value (Double)
short othercount = readNextShort();
readData(25 * othercount);
}
}
else
{
//Minecraft 1.6.0 / 1.6.1
readNextInt(); //Entity ID
int count = readNextInt();
for (int i = 0; i < count; i++)
{
readNextString(); //Property name
readData(8); //Property value (Double)
}
}
}
}
private void readNextWindowData(int protocolversion)
{
readData(1);
byte windowtype = readNextByte();
readNextString();
readData(1);
if (protocolversion > 51)
{
readData(1);
if (protocolversion >= 72 && windowtype == 0xb)
{
readNextInt();
}
}
}
private void readNextChunkBulkData()
{
short chunkcount = readNextShort();
int datalen = readNextInt();
readData(1);
readData(datalen);
readData(12 * (chunkcount));
}
private void setcolor(char c)
{
switch (c)
{
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;
}
}
private void printstring(string str, bool acceptnewlines)
{
if (str != "")
{
char prev = ' ';
foreach (char c in str)
{
if (c == '§')
{
prev = c;
continue;
}
else if (prev == '§')
{
setcolor(c);
prev = c;
}
else
{
if (c == '\n' && !acceptnewlines) { continue; }
else ConsoleIO.Write(c);
}
}
ConsoleIO.Write('\n');
}
Console.ForegroundColor = ConsoleColor.Gray;
}
private string ReverseString(string a)
{
char[] tmp = a.ToCharArray();
Array.Reverse(tmp);
return new string(tmp);
}
private string ByteArrayToString(byte[] ba)
{
string conv = "";
for (int i = 1; i < ba.Length; i += 2)
{
conv += (char)ba[i];
}
return conv;
}
public void setVersion(byte ver) { protocolversion = ver; }
public void setClient(TcpClient n) { c = n; }
private void setEncryptedClient(Crypto.AesStream n) { s = n; encrypted = true; }
private void Receive(byte[] buffer, int start, int offset, SocketFlags f)
{
while (c.Client.Available < start + offset) { }
if (encrypted)
{
s.Read(buffer, start, offset);
}
else c.Client.Receive(buffer, start, offset, f);
}
private void Send(byte[] buffer)
{
if (encrypted)
{
s.Write(buffer, 0, buffer.Length);
}
else c.Client.Send(buffer);
}
public static bool GetServerInfo(string serverIP, ref byte protocolversion, ref string version)
{
try
{
string host; int port;
string[] sip = serverIP.Split(':');
host = sip[0];
if (sip.Length == 1)
{
port = 25565;
}
else
{
try
{
port = Convert.ToInt32(sip[1]);
}
catch (FormatException) { port = 25565; }
}
TcpClient tcp = new TcpClient(host, port);
byte[] ping = new byte[2] { 0xfe, 0x01 };
tcp.Client.Send(ping, SocketFlags.None);
tcp.Client.Receive(ping, 0, 1, SocketFlags.None);
if (ping[0] == 0xff)
{
MinecraftCom ComTmp = new MinecraftCom();
ComTmp.setClient(tcp);
string result = ComTmp.readNextString();
Console.ForegroundColor = ConsoleColor.DarkGray;
//Console.WriteLine(result.Replace((char)0x00, ' '));
if (result.Length > 2 && result[0] == '§' && result[1] == '1')
{
string[] tmp = result.Split((char)0x00);
protocolversion = (byte)Int16.Parse(tmp[1]);
version = tmp[2];
}
else
{
protocolversion = (byte)39;
version = "B1.8.1 - 1.3.2";
}
Console.WriteLine("Server version : MC " + version + " (protocol v" + protocolversion + ").");
Console.ForegroundColor = ConsoleColor.Gray;
return true;
}
else
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("Unexpected answer from the server (is that a Minecraft server ?)");
Console.ForegroundColor = ConsoleColor.Gray;
return false;
}
}
catch
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("An error occured while attempting to connect to this IP.");
Console.ForegroundColor = ConsoleColor.Gray;
return false;
}
}
public bool Handshake(string username, string sessionID, ref string serverID, ref byte[] token, string host, int port)
{
username = ReverseString(username);
//array
byte[] data = new byte[10 + (username.Length * 2) + (host.Length * 2)];
//packet id
data[0] = (byte)2;
//Protocol Version - Minecraft 1.3.1 & 1.3.2
//data[1] = (byte)39;
//Protocol Version - Minecraft 1.4.2
//data[1] = (byte)47;
//Protocol Version - Minecraft 1.4.4
//data[1] = (byte)49;
//Protocol Version - Minecraft 1.4.6 & 1.4.7
//data[1] = (byte)51;
//Protocol Version - Minecraft 1.5.0
//data[1] = (byte)60;
//Protocol Version - Custom
data[1] = protocolversion;
//short len
byte[] sh = new byte[2];
sh = BitConverter.GetBytes((short)username.Length);
Array.Reverse(sh);
sh.CopyTo(data, 2);
//username
byte[] name = Encoding.Unicode.GetBytes(username);
Array.Reverse(name);
name.CopyTo(data, 4);
//short len
sh = new byte[2];
sh = BitConverter.GetBytes((short)host.Length);
Array.Reverse(sh);
sh.CopyTo(data, 4 + (username.Length * 2));
//host
byte[] bhost = Encoding.Unicode.GetBytes(username);
Array.Reverse(bhost);
bhost.CopyTo(data, 6 + (username.Length * 2));
//port
sh = new byte[4];
sh = BitConverter.GetBytes(port);
Array.Reverse(sh);
sh.CopyTo(data, 6 + (username.Length * 2) + (host.Length * 2));
Send(data);
byte[] pid = new byte[1];
Receive(pid, 0, 1, SocketFlags.None);
while (pid[0] == 0xFA) //Skip some early plugin messages
{
processPacket(pid[0]);
Receive(pid, 0, 1, SocketFlags.None);
}
if (pid[0] == 0xFD)
{
serverID = readNextString();
byte[] Serverkey_RAW = readNextByteArray();
token = readNextByteArray();
if (serverID == "-")
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("Server is in offline mode.");
Console.ForegroundColor = ConsoleColor.Gray;
return true; //No need to check session or start encryption
}
else
{
var PublicServerkey = Crypto.GenerateRSAPublicKey(Serverkey_RAW);
var SecretKey = Crypto.GenerateAESPrivateKey();
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("Handshake sussessful. (Server ID: " + serverID + ')');
Console.ForegroundColor = ConsoleColor.Gray;
return StartEncryption(ReverseString(username), sessionID, token, serverID, PublicServerkey, SecretKey);
}
}
else return false;
}
public bool StartEncryption(string username, string sessionID, byte[] token, string serverIDhash, java.security.PublicKey serverKey, javax.crypto.SecretKey secretKey)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
ConsoleIO.WriteLine("Crypto keys & hash generated.");
Console.ForegroundColor = ConsoleColor.Gray;
if (serverIDhash != "-")
{
Console.WriteLine("Checking Session...");
if (!SessionCheck(username, sessionID, new java.math.BigInteger(Crypto.getServerHash(serverIDhash, serverKey, secretKey)).toString(16)))
{
return false;
}
}
//Encrypt the data
byte[] key_enc = Crypto.Encrypt(serverKey, secretKey.getEncoded());
byte[] token_enc = Crypto.Encrypt(serverKey, token);
byte[] keylen = BitConverter.GetBytes((short)key_enc.Length);
byte[] tokenlen = BitConverter.GetBytes((short)token_enc.Length);
Array.Reverse(keylen);
Array.Reverse(tokenlen);
//Building the packet
byte[] data = new byte[5 + (short)key_enc.Length + (short)token_enc.Length];
data[0] = 0xFC;
keylen.CopyTo(data, 1);
key_enc.CopyTo(data, 3);
tokenlen.CopyTo(data, 3 + (short)key_enc.Length);
token_enc.CopyTo(data, 5 + (short)key_enc.Length);
//Send it back
Send(data);
//Getting the next packet
byte[] pid = new byte[1];
Receive(pid, 0, 1, SocketFlags.None);
if (pid[0] == 0xFC)
{
readData(4);
setEncryptedClient(Crypto.SwitchToAesMode(c.GetStream(), secretKey));
return true;
}
else return false;
}
public bool FinalizeLogin()
{
//Creating byte array
byte[] data = new byte[2];
data[0] = 0xCD;
data[1] = 0;
Send(data);
try
{
byte[] pid = new byte[1];
try
{
if (c.Connected)
{
Receive(pid, 0, 1, SocketFlags.None);
while (pid[0] >= 0xC0 && pid[0] != 0xFF) //Skip some early packets or plugin messages
{
processPacket(pid[0]);
Receive(pid, 0, 1, SocketFlags.None);
}
if (pid[0] == (byte)1)
{
readData(4); readNextString(); readData(5);
return true; //The Server accepted the request
}
else if (pid[0] == (byte)0xFF)
{
string reason = readNextString();
Console.WriteLine("Login rejected by Server :"); printstring(reason, true);
for (int i = 0; i < bots.Count; i++) { bots[i].OnDisconnect(ChatBot.DisconnectReason.LoginRejected, reason); }
return false;
}
}
}
catch
{
//Connection failed
return false;
}
}
catch
{
//Network error
Console.WriteLine("Connection Lost.");
return false;
}
return false; //Login was unsuccessful (received a kick...)
}
public bool SendChatMessage(string message)
{
try
{
message = ReverseString(message);
byte[] chat = new byte[3 + (message.Length * 2)];
chat[0] = (byte)3;
byte[] msglen = new byte[2];
msglen = BitConverter.GetBytes((short)message.Length);
Array.Reverse(msglen);
msglen.CopyTo(chat, 1);
byte[] msg = new byte[message.Length * 2];
msg = Encoding.Unicode.GetBytes(message);
Array.Reverse(msg);
msg.CopyTo(chat, 3);
Send(chat);
return true;
}
catch (SocketException) { return false; }
}
public void Disconnect(string message)
{
try
{
message = ReverseString(message);
byte[] reason = new byte[3 + (message.Length * 2)];
reason[0] = (byte)0xff;
byte[] msglen = new byte[2];
msglen = BitConverter.GetBytes((short)message.Length);
Array.Reverse(msglen);
msglen.CopyTo(reason, 1);
byte[] msg = new byte[message.Length * 2];
msg = Encoding.Unicode.GetBytes(message);
Array.Reverse(msg);
msg.CopyTo(reason, 3);
Send(reason);
}
catch (SocketException) { }
catch (System.IO.IOException) { }
}
private List<ChatBot> bots = new List<ChatBot>();
public void BotLoad(ChatBot b) { b.SetHandler(this); bots.Add(b); b.Initialize(); }
public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); }
public void BotClear() { bots.Clear(); }
}
}

280
MinecraftClient/Program.cs Normal file
View file

@ -0,0 +1,280 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MinecraftClient
{
/// <summary>
/// Minecraft Console Client by ORelio (c) 2012-2013.
/// Allows to connect to any Minecraft server, send and receive text, automated scripts.
/// This source code is released under the CDDL 1.0 License.
/// </summary>
class Program
{
private static McTcpClient Client;
private static string loginusername = "";
private static string user = "";
private static string pass = "";
private static string ip = "";
private static string command = "";
private static string[] startupargs;
/// <summary>
/// The main entry point of Minecraft Console Client
/// </summary>
static void Main(string[] args)
{
Console.WriteLine("Console Client for MC 1.4.6 to 1.6.2 - v1.5.2 - By ORelio (or3L1o@live.fr)");
//Processing Command-line arguments
if (args.Length >= 1)
{
user = args[0];
if (args.Length >= 2)
{
pass = args[1];
if (args.Length >= 3)
{
ip = args[2];
if (args.Length >= 4)
{
command = args[3];
}
}
}
}
//Asking the user to type in missing data such as Username and Password
if (user == "") {
Console.Write("Username : ");
user = Console.ReadLine();
}
if (pass == "") {
Console.Write("Password : ");
pass = Console.ReadLine();
//Hide the password
Console.CursorTop--;
Console.Write("Password : <******>");
for (int i = 19; i < Console.BufferWidth; i++) { Console.Write(' '); }
}
//Save the arguments
startupargs = args;
loginusername = user;
//Start the Client
InitializeClient();
}
/// <summary>
/// Start a new Client
/// </summary>
private static void InitializeClient()
{
MinecraftCom.LoginResult result;
string logindata = "";
if (pass == "-")
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("You chose to run in offline mode.");
Console.ForegroundColor = ConsoleColor.Gray;
result = MinecraftCom.LoginResult.Success;
logindata = "0:deprecated:" + user + ":0";
}
else
{
Console.WriteLine("Connecting to Minecraft.net...");
result = MinecraftCom.GetLogin(loginusername, pass, ref logindata);
}
if (result == MinecraftCom.LoginResult.Success)
{
user = logindata.Split(':')[2];
string sessionID = logindata.Split(':')[3];
Console.WriteLine("Success. (session ID: " + sessionID + ')');
if (ip == "")
{
Console.Write("Server IP : ");
ip = Console.ReadLine();
}
//Get server version
Console.WriteLine("Retrieving Server Info...");
byte protocolversion = 0; string version = "";
if (MinecraftCom.GetServerInfo(ip, ref protocolversion, ref version))
{
//Supported protocol version ?
int[] supportedVersions = { 51, 60, 61, 72, 73, 74 };
if (Array.IndexOf(supportedVersions, protocolversion) > -1)
{
//Minecraft 1.6+ ? Load translations
if (protocolversion >= 72) { ChatParser.InitTranslations(); }
//Will handle the connection for this client
Console.WriteLine("Version is supported.");
MinecraftCom handler = new MinecraftCom();
handler.setVersion(protocolversion);
//Load & initialize bots if needed
foreach (string arg in startupargs)
{
if (arg.Length > 4 && arg.Substring(0, 4).ToLower() == "bot:")
{
int param;
string[] botargs = arg.ToLower().Split(':');
switch (botargs[1])
{
case "antiafk":
#region Arguments for the AntiAFK bot
param = 600;
if (botargs.Length > 2)
{
try { param = Convert.ToInt32(botargs[2]); }
catch (FormatException) { }
}
#endregion
handler.BotLoad(new Bots.AntiAFK(param)); break;
case "pendu": handler.BotLoad(new Bots.Pendu(false)); break;
case "hangman": handler.BotLoad(new Bots.Pendu(true)); break;
case "alerts": handler.BotLoad(new Bots.Alerts()); break;
case "log":
#region Arguments for the ChatLog bot
bool datetime = true;
string file = "chat-" + ip + ".log";
Bots.ChatLog.MessageFilter filter = Bots.ChatLog.MessageFilter.AllMessages;
if (botargs.Length > 2)
{
datetime = (botargs[2] != "0");
if (botargs.Length > 3)
{
switch (botargs[3])
{
case "all": filter = Bots.ChatLog.MessageFilter.AllText; break;
case "messages": filter = Bots.ChatLog.MessageFilter.AllMessages; break;
case "chat": filter = Bots.ChatLog.MessageFilter.OnlyChat; break;
case "private": filter = Bots.ChatLog.MessageFilter.OnlyWhispers; break;
}
if (botargs.Length > 4 && botargs[4] != "") { file = botargs[4]; }
}
}
#endregion
handler.BotLoad(new Bots.ChatLog(file, filter, datetime)); break;
case "logplayerlist":
#region Arguments for the PlayerListLogger bot
param = 600;
if (botargs.Length > 2)
{
try { param = Convert.ToInt32(botargs[2]); }
catch (FormatException) { }
}
#endregion
handler.BotLoad(new Bots.PlayerListLogger(param, "connected-" + ip + ".log")); break;
case "autorelog":
#region Arguments for the AutoRelog bot
int delay = 10;
if (botargs.Length > 2)
{
try { delay = Convert.ToInt32(botargs[2]); }
catch (FormatException) { }
}
int retries = 3;
if (botargs.Length > 3)
{
try { retries = Convert.ToInt32(botargs[3]); }
catch (FormatException) { }
}
#endregion
handler.BotLoad(new Bots.AutoRelog(delay, retries)); break;
case "xauth":
if (botargs.Length > 2) { handler.BotLoad(new Bots.xAuth(botargs[2])); } break;
}
command = "";
}
}
//Start the main TCP client
if (command != "")
{
Client = new McTcpClient(user, sessionID, ip, handler, command);
}
else Client = new McTcpClient(user, sessionID, ip, handler);
}
else
{
Console.WriteLine("Cannot connect to the server : This version is not supported !");
ReadLineReconnect();
}
}
else
{
Console.WriteLine("Failed to ping this IP.");
ReadLineReconnect();
}
}
else
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write("Connection failed : ");
switch (result)
{
case MinecraftCom.LoginResult.AccountMigrated: Console.WriteLine("Account migrated, use e-mail as username."); break;
case MinecraftCom.LoginResult.Blocked: Console.WriteLine("Too many failed logins. Please try again later."); break;
case MinecraftCom.LoginResult.WrongPassword: Console.WriteLine("Incorrect password."); break;
case MinecraftCom.LoginResult.NotPremium: Console.WriteLine("User not premium."); break;
case MinecraftCom.LoginResult.Error: Console.WriteLine("Network error."); break;
}
while (Console.KeyAvailable) { Console.ReadKey(false); }
if (command == "") { ReadLineReconnect(); }
}
}
/// <summary>
/// Disconnect the current client from the server and restart it
/// </summary>
public static void Restart()
{
new System.Threading.Thread(new System.Threading.ThreadStart(t_restart)).Start();
}
/// <summary>
/// Pause the program, usually when an error or a kick occured, letting the user press Enter to quit OR type /reconnect
/// </summary>
/// <returns>Return True if the user typed "/reconnect"</returns>
public static bool ReadLineReconnect()
{
string text = Console.ReadLine();
if (text == "reco" || text == "reconnect" || text == "/reco" || text == "/reconnect")
{
Program.Restart();
return true;
}
else return false;
}
/// <summary>
/// Private thread for restarting the program. Called through Restart()
/// </summary>
private static void t_restart()
{
if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); }
Console.WriteLine("Restarting Minecraft Console Client...");
InitializeClient();
}
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Minecraft Console Client")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MinecraftClient")]
[assembly: AssemblyCopyright("Copyright © ORelio 2012-2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("78af6200-1f48-4daa-b473-109a9728b61f")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3")]
[assembly: AssemblyFileVersion("1.3")]