mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-11-07 17:36:07 +00:00
Merge pull request #132 from ORelio/Indev
Merge changes for v1.9.0 BETA
This commit is contained in:
commit
7ad0ae58e4
72 changed files with 8346 additions and 2148 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -3,3 +3,4 @@
|
|||
/MinecraftClientGUI.v11.suo
|
||||
/MinecraftClientGUI.suo
|
||||
/MinecraftClient.userprefs
|
||||
/.vs/
|
||||
|
|
|
|||
374
MinecraftClient/CSharpRunner.cs
Normal file
374
MinecraftClient/CSharpRunner.cs
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using Microsoft.CSharp;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// C# Script runner - Compile on-the-fly and run C# scripts
|
||||
/// </summary>
|
||||
class CSharpRunner
|
||||
{
|
||||
private static readonly Dictionary<ulong, Assembly> CompileCache = new Dictionary<ulong, Assembly>();
|
||||
|
||||
/// <summary>
|
||||
/// Run the specified C# script file
|
||||
/// </summary>
|
||||
/// <param name="apiHandler">ChatBot handler for accessing ChatBot API</param>
|
||||
/// <param name="tickHandler">Tick handler for waiting after some API calls</param>
|
||||
/// <param name="lines">Lines of the script file to run</param>
|
||||
/// <param name="args">Arguments to pass to the script</param>
|
||||
/// <param name="run">Set to false to compile and cache the script without launching it</param>
|
||||
/// <exception cref="CSharpException">Thrown if an error occured</exception>
|
||||
/// <returns>Result of the execution, returned by the script</returns>
|
||||
public static object Run(ChatBot apiHandler, ManualResetEvent tickHandler, string[] lines, string[] args, bool run = true)
|
||||
{
|
||||
//Script compatibility check for handling future versions differently
|
||||
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
|
||||
throw new CSharpException(CSErrorType.InvalidScript,
|
||||
new InvalidDataException("The provided script does not have a valid MCCScript header"));
|
||||
|
||||
//Script hash for determining if it was previously compiled
|
||||
ulong scriptHash = QuickHash(lines);
|
||||
Assembly assembly = null;
|
||||
|
||||
//No need to compile two scripts at the same time
|
||||
lock (CompileCache)
|
||||
{
|
||||
///Process and compile script only if not already compiled
|
||||
if (!Settings.CacheScripts || !CompileCache.ContainsKey(scriptHash))
|
||||
{
|
||||
//Process different sections of the script file
|
||||
bool scriptMain = true;
|
||||
List<string> script = new List<string>();
|
||||
List<string> extensions = new List<string>();
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line.StartsWith("//MCCScript"))
|
||||
{
|
||||
if (line.EndsWith("Extensions"))
|
||||
scriptMain = false;
|
||||
}
|
||||
else if (scriptMain)
|
||||
script.Add(line);
|
||||
else extensions.Add(line);
|
||||
}
|
||||
|
||||
//Add return statement if missing
|
||||
if (script.All(line => !line.StartsWith("return ") && !line.Contains(" return ")))
|
||||
script.Add("return null;");
|
||||
|
||||
//Generate a class from the given script
|
||||
string code = String.Join("\n", new string[]
|
||||
{
|
||||
"using System;",
|
||||
"using System.Collections.Generic;",
|
||||
"using System.Text.RegularExpressions;",
|
||||
"using System.Linq;",
|
||||
"using System.Text;",
|
||||
"using System.IO;",
|
||||
"using System.Threading;",
|
||||
"using MinecraftClient;",
|
||||
"using MinecraftClient.Mapping;",
|
||||
"namespace ScriptLoader {",
|
||||
"public class Script {",
|
||||
"public CSharpAPI MCC;",
|
||||
"public object __run(CSharpAPI __apiHandler, string[] args) {",
|
||||
"this.MCC = __apiHandler;",
|
||||
String.Join("\n", script),
|
||||
"}",
|
||||
String.Join("\n", extensions),
|
||||
"}}",
|
||||
});
|
||||
|
||||
//Compile the C# class in memory using all the currently loaded assemblies
|
||||
CSharpCodeProvider compiler = new CSharpCodeProvider();
|
||||
CompilerParameters parameters = new CompilerParameters();
|
||||
parameters.ReferencedAssemblies
|
||||
.AddRange(AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.Where(a => !a.IsDynamic)
|
||||
.Select(a => a.Location).ToArray());
|
||||
parameters.CompilerOptions = "/t:library";
|
||||
parameters.GenerateInMemory = true;
|
||||
CompilerResults result = compiler.CompileAssemblyFromSource(parameters, code);
|
||||
|
||||
//Process compile warnings and errors
|
||||
if (result.Errors.Count > 0)
|
||||
throw new CSharpException(CSErrorType.LoadError,
|
||||
new InvalidOperationException(result.Errors[0].ErrorText));
|
||||
|
||||
//Retrieve compiled assembly
|
||||
assembly = result.CompiledAssembly;
|
||||
if (Settings.CacheScripts)
|
||||
CompileCache[scriptHash] = result.CompiledAssembly;
|
||||
}
|
||||
else if (Settings.CacheScripts)
|
||||
assembly = CompileCache[scriptHash];
|
||||
}
|
||||
|
||||
//Run the compiled assembly with exception handling
|
||||
if (run)
|
||||
{
|
||||
try
|
||||
{
|
||||
object compiledScript
|
||||
= CompileCache[scriptHash].CreateInstance("ScriptLoader.Script");
|
||||
return
|
||||
compiledScript
|
||||
.GetType()
|
||||
.GetMethod("__run")
|
||||
.Invoke(compiledScript,
|
||||
new object[] { new CSharpAPI(apiHandler, tickHandler), args });
|
||||
}
|
||||
catch (Exception e) { throw new CSharpException(CSErrorType.RuntimeError, e); }
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quickly calculate a hash for the given script
|
||||
/// </summary>
|
||||
/// <param name="lines">script lines</param>
|
||||
/// <returns>Quick hash as unsigned long</returns>
|
||||
private static ulong QuickHash(string[] lines)
|
||||
{
|
||||
ulong hashedValue = 3074457345618258791ul;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < lines[i].Length; j++)
|
||||
{
|
||||
hashedValue += lines[i][j];
|
||||
hashedValue *= 3074457345618258799ul;
|
||||
}
|
||||
hashedValue += '\n';
|
||||
hashedValue *= 3074457345618258799ul;
|
||||
}
|
||||
return hashedValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describe a C# script error type
|
||||
/// </summary>
|
||||
public enum CSErrorType { FileReadError, InvalidScript, LoadError, RuntimeError };
|
||||
|
||||
/// <summary>
|
||||
/// Describe a C# script error with associated error type
|
||||
/// </summary>
|
||||
public class CSharpException : Exception
|
||||
{
|
||||
private CSErrorType _type;
|
||||
public CSErrorType ExceptionType { get { return _type; } }
|
||||
public override string Message { get { return InnerException.Message; } }
|
||||
public override string ToString() { return InnerException.ToString(); }
|
||||
public CSharpException(CSErrorType type, Exception inner)
|
||||
: base(inner != null ? inner.Message : "", inner)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the C# API object accessible from C# Scripts
|
||||
/// </summary>
|
||||
public class CSharpAPI : ChatBot
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread blocking utility for stopping execution when making a ChatBot API call
|
||||
/// </summary>
|
||||
private ManualResetEvent tickHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new C# API Wrapper
|
||||
/// </summary>
|
||||
/// <param name="apiHandler">ChatBot API Handler</param>
|
||||
/// <param name="tickHandler">ChatBot tick handler</param>
|
||||
public CSharpAPI(ChatBot apiHandler, ManualResetEvent tickHandler)
|
||||
{
|
||||
SetMaster(apiHandler);
|
||||
this.tickHandler = tickHandler;
|
||||
}
|
||||
|
||||
/* == Wrappers for ChatBot API with public visibility and call limit to one per tick for safety == */
|
||||
|
||||
/// <summary>
|
||||
/// Write some text in the console. Nothing will be sent to the server.
|
||||
/// </summary>
|
||||
/// <param name="text">Log text to write</param>
|
||||
new public void LogToConsole(object text)
|
||||
{
|
||||
base.LogToConsole(text);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <returns>True if the text was sent with no error</returns>
|
||||
public bool SendText(object text)
|
||||
{
|
||||
bool result = base.SendText(text is string ? (string)text : text.ToString());
|
||||
tickHandler.WaitOne();
|
||||
Thread.Sleep(1000);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
|
||||
/// </summary>
|
||||
/// <param name="command">The command to process</param>
|
||||
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
|
||||
new public bool PerformInternalCommand(string command)
|
||||
{
|
||||
bool result = base.PerformInternalCommand(command);
|
||||
tickHandler.WaitOne();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server and restart the program
|
||||
/// It will unload and reload all the bots and then reconnect to the server
|
||||
/// </summary>
|
||||
/// <param name="extraAttempts">If connection fails, the client will make X extra attempts</param>
|
||||
new public void ReconnectToTheServer(int extraAttempts = -999999)
|
||||
{
|
||||
if (extraAttempts == -999999)
|
||||
base.ReconnectToTheServer();
|
||||
else base.ReconnectToTheServer(extraAttempts);
|
||||
tickHandler.WaitOne();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server and exit the program
|
||||
/// </summary>
|
||||
new public void DisconnectAndExit()
|
||||
{
|
||||
base.DisconnectAndExit();
|
||||
tickHandler.WaitOne();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the provided ChatBot object
|
||||
/// </summary>
|
||||
/// <param name="bot">Bot to load</param>
|
||||
new public void LoadBot(ChatBot bot)
|
||||
{
|
||||
base.LoadBot(bot);
|
||||
tickHandler.WaitOne();
|
||||
}
|
||||
|
||||
/* == Additional Methods useful for Script API == */
|
||||
|
||||
/// <summary>
|
||||
/// Get a global variable by name
|
||||
/// </summary>
|
||||
/// <param name="varName">Name of the variable</param>
|
||||
/// <returns>Value of the variable or null if no variable</returns>
|
||||
public object GetVar(string varName)
|
||||
{
|
||||
return Settings.GetVar(varName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a global variable by name, as a string
|
||||
/// </summary>
|
||||
/// <param name="varName">Name of the variable</param>
|
||||
/// <returns>Value of the variable as string, or null if no variable</returns>
|
||||
public string GetVarAsString(string varName)
|
||||
{
|
||||
object val = GetVar(varName);
|
||||
if (val != null)
|
||||
return val.ToString();
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a global variable by name, as an integer
|
||||
/// </summary>
|
||||
/// <param name="varName">Name of the variable</param>
|
||||
/// <returns>Value of the variable as int, or 0 if no variable or not a number</returns>
|
||||
public int GetVarAsInt(string varName)
|
||||
{
|
||||
if (GetVar(varName) is int)
|
||||
return (int)GetVar(varName);
|
||||
int result;
|
||||
if (int.TryParse(GetVarAsString(varName), out result))
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a global variable by name, as a boolean
|
||||
/// </summary>
|
||||
/// <param name="varName">Name of the variable</param>
|
||||
/// <returns>Value of the variable as bool, or false if no variable or not a boolean</returns>
|
||||
public bool GetVarAsBool(string varName)
|
||||
{
|
||||
if (GetVar(varName) is bool)
|
||||
return (bool)GetVar(varName);
|
||||
bool result;
|
||||
if (bool.TryParse(GetVarAsString(varName), out result))
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a global variable for further use in any other script
|
||||
/// </summary>
|
||||
/// <param name="varName">Name of the variable</param>
|
||||
/// <param name="varValue">Value of the variable</param>
|
||||
public bool SetVar(string varName, object varValue)
|
||||
{
|
||||
return Settings.SetVar(varName, varValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load login/password using an account alias and optionally reconnect to the server
|
||||
/// </summary>
|
||||
/// <param name="accountAlias">Account alias</param>
|
||||
/// <param name="andReconnect">Set to true to reconnecto to the server afterwards</param>
|
||||
/// <returns>True if the account was found and loaded</returns>
|
||||
public bool SetAccount(string accountAlias, bool andReconnect = false)
|
||||
{
|
||||
bool result = Settings.SetAccount(accountAlias);
|
||||
if (result && andReconnect)
|
||||
ReconnectToTheServer();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load new server information and optionally reconnect to the server
|
||||
/// </summary>
|
||||
/// <param name="server">"serverip:port" couple or server alias</param>
|
||||
/// <returns>True if the server IP was valid and loaded, false otherwise</returns>
|
||||
public bool SetServer(string server, bool andReconnect = false)
|
||||
{
|
||||
bool result = Settings.SetServerIP(server);
|
||||
if (result && andReconnect)
|
||||
ReconnectToTheServer();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously call another script and retrieve the result
|
||||
/// </summary>
|
||||
/// <param name="script">Script to call</param>
|
||||
/// <param name="args">Arguments to pass to the script</param>
|
||||
/// <returns>An object returned by the script, or null</returns>
|
||||
public object CallScript(string script, string[] args)
|
||||
{
|
||||
string[] lines = null;
|
||||
ChatBots.Script.LookForScript(ref script);
|
||||
try { lines = File.ReadAllLines(script); }
|
||||
catch (Exception e) { throw new CSharpException(CSErrorType.FileReadError, e); }
|
||||
return CSharpRunner.Run(this, tickHandler, lines, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
|
|
@ -14,12 +16,12 @@ namespace MinecraftClient
|
|||
/// Once your bot is created, read the explanations below to start using it in the MinecraftClient app.
|
||||
///
|
||||
/// Pieces of code to add in other parts of the program for your bot. Line numbers are approximative.
|
||||
/// McTcpClient:110 | if (Settings.YourBot_Enabled) { handler.BotLoad(new ChatBots.YourBot()); }
|
||||
/// Settings.cs:73 | public static bool YourBot_Enabled = false;
|
||||
/// Settings.cs:74 | private enum ParseMode { /* [...] */, YourBot };
|
||||
/// Settings.cs:106 | case "yourbot": pMode = ParseMode.YourBot; break;
|
||||
/// Settings.cs:197 | case ParseMode.YourBot: switch (argName.ToLower()) { case "enabled": YourBot_Enabled = str2bool(argValue); break; } break;
|
||||
/// Settings.cs:267 | + "[YourBot]\r\n" + "enabled=false\r\n"
|
||||
/// McTcpClient:110 | if (Settings.YourBot_Enabled) { handler.BotLoad(new ChatBots.YourBot()); }
|
||||
/// Here your are. Now you will have a setting in MinecraftClient.ini for enabling your brand new bot.
|
||||
/// Delete MinecraftClient.ini to re-generate it or add the lines [YourBot] and enabled=true to the existing one.
|
||||
///
|
||||
|
|
@ -32,9 +34,40 @@ namespace MinecraftClient
|
|||
{
|
||||
public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost };
|
||||
|
||||
//Will be automatically set on bot loading, don't worry about this
|
||||
public void SetHandler(McTcpClient handler) { this.handler = handler; }
|
||||
private McTcpClient handler;
|
||||
//Handler will be automatically set on bot loading, don't worry about this
|
||||
public void SetHandler(McTcpClient handler) { this._handler = handler; }
|
||||
protected void SetMaster(ChatBot master) { this.master = master; }
|
||||
protected void LoadBot(ChatBot bot) { Handler.BotUnLoad(bot); Handler.BotLoad(bot); }
|
||||
private McTcpClient Handler { get { return master != null ? master.Handler : _handler; } }
|
||||
private McTcpClient _handler = null;
|
||||
private ChatBot master = null;
|
||||
private List<string> registeredPluginChannels = new List<String>();
|
||||
private Queue<string> chatQueue = new Queue<string>();
|
||||
private DateTime lastMessageSentTime = DateTime.MinValue;
|
||||
private bool CanSendTextNow
|
||||
{
|
||||
get
|
||||
{
|
||||
return DateTime.Now > lastMessageSentTime + Settings.botMessageDelay;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the current chat message queue, displaying a message after enough time passes.
|
||||
/// </summary>
|
||||
internal void ProcessQueuedText()
|
||||
{
|
||||
if (chatQueue.Count > 0)
|
||||
{
|
||||
if (CanSendTextNow)
|
||||
{
|
||||
string text = chatQueue.Dequeue();
|
||||
LogToConsole("Sending '" + text + "'");
|
||||
lastMessageSentTime = DateTime.Now;
|
||||
Handler.SendText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Main methods to override for creating your bot */
|
||||
|
|
@ -42,10 +75,22 @@ namespace MinecraftClient
|
|||
|
||||
/// <summary>
|
||||
/// Anything you want to initialize your bot, will be called on load by MinecraftCom
|
||||
///
|
||||
/// NOTE: Chat messages cannot be sent at this point in the login process. If you want to send
|
||||
/// a message when the bot is loaded, use AfterGameJoined.
|
||||
/// </summary>
|
||||
|
||||
public virtual void Initialize() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called after the server has been joined successfully and chat messages are able to be sent.
|
||||
///
|
||||
/// NOTE: This is not always right after joining the server - if the bot was loaded after logging
|
||||
/// in this is still called.
|
||||
/// </summary>
|
||||
|
||||
public virtual void AfterGameJoined() { }
|
||||
|
||||
/// <summary>
|
||||
/// Will be called every ~100ms (10fps) if loaded in MinecraftCom
|
||||
/// </summary>
|
||||
|
|
@ -68,6 +113,17 @@ namespace MinecraftClient
|
|||
|
||||
public virtual bool OnDisconnect(DisconnectReason reason, string message) { return false; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when a plugin channel message is received.
|
||||
/// The given channel must have previously been registered with RegisterPluginChannel.
|
||||
/// This can be used to communicate with server mods or plugins. See wiki.vg for more
|
||||
/// information about plugin channels: http://wiki.vg/Plugin_channel
|
||||
/// </summary>
|
||||
/// <param name="channel">The name of the channel</param>
|
||||
/// <param name="data">The payload for the message</param>
|
||||
|
||||
public virtual void OnPluginMessage(string channel, byte[] data) { }
|
||||
|
||||
/* =================================================================== */
|
||||
/* ToolBox - Methods below might be useful while creating your bot. */
|
||||
/* You should not need to interact with other classes of the program. */
|
||||
|
|
@ -78,12 +134,25 @@ namespace MinecraftClient
|
|||
/// 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>
|
||||
/// <param name="sendImmediately">Whether the message should be sent immediately rather than being queued to avoid chat spam</param>
|
||||
/// <returns>True if the text was sent with no error</returns>
|
||||
|
||||
protected bool SendText(string text)
|
||||
protected bool SendText(string text, bool sendImmediately = false)
|
||||
{
|
||||
if (Settings.botMessageDelay.TotalSeconds > 0 && !sendImmediately)
|
||||
{
|
||||
if (!CanSendTextNow)
|
||||
{
|
||||
chatQueue.Enqueue(text);
|
||||
// TODO: We don't know whether there was an error at this point, so we assume there isn't.
|
||||
// Might not be the best idea.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LogToConsole("Sending '" + text + "'");
|
||||
return handler.SendText(text);
|
||||
lastMessageSentTime = DateTime.Now;
|
||||
return Handler.SendText(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -92,10 +161,10 @@ namespace MinecraftClient
|
|||
/// <param name="command">The command to process</param>
|
||||
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
|
||||
|
||||
protected bool performInternalCommand(string command)
|
||||
protected bool PerformInternalCommand(string command)
|
||||
{
|
||||
string temp = "";
|
||||
return handler.performInternalCommand(command, ref temp);
|
||||
return Handler.PerformInternalCommand(command, ref temp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -105,16 +174,16 @@ namespace MinecraftClient
|
|||
/// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param>
|
||||
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
|
||||
|
||||
protected bool performInternalCommand(string command, ref string response_msg)
|
||||
protected bool PerformInternalCommand(string command, ref string response_msg)
|
||||
{
|
||||
return handler.performInternalCommand(command, ref response_msg);
|
||||
return Handler.PerformInternalCommand(command, ref response_msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove color codes ("§c") from a text message received from the server
|
||||
/// </summary>
|
||||
|
||||
protected static string getVerbatim(string text)
|
||||
protected static string GetVerbatim(string text)
|
||||
{
|
||||
if ( String.IsNullOrEmpty(text) )
|
||||
return String.Empty;
|
||||
|
|
@ -135,13 +204,13 @@ namespace MinecraftClient
|
|||
/// Verify that a string contains only a-z A-Z 0-9 and _ characters.
|
||||
/// </summary>
|
||||
|
||||
protected static bool isValidName(string username)
|
||||
protected static bool IsValidName(string username)
|
||||
{
|
||||
if ( String.IsNullOrEmpty(username) )
|
||||
if (String.IsNullOrEmpty(username))
|
||||
return false;
|
||||
|
||||
foreach ( char c in username )
|
||||
if ( !((c >= 'a' && c <= 'z')
|
||||
foreach (char c in username)
|
||||
if (!((c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
|| (c >= '0' && c <= '9')
|
||||
|| c == '_') )
|
||||
|
|
@ -158,75 +227,108 @@ namespace MinecraftClient
|
|||
/// <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)
|
||||
protected static bool IsPrivateMessage(string text, ref string message, ref string sender)
|
||||
{
|
||||
text = getVerbatim(text);
|
||||
if (text == "") { return false; }
|
||||
string[] tmp = text.Split(' ');
|
||||
if (String.IsNullOrEmpty(text))
|
||||
return false;
|
||||
|
||||
try
|
||||
text = GetVerbatim(text);
|
||||
|
||||
//Built-in detection routine for private messages
|
||||
if (Settings.ChatFormat_Builtins)
|
||||
{
|
||||
//Detect vanilla /tell messages
|
||||
//Someone whispers message (MC 1.5)
|
||||
//Someone whispers to you: message (MC 1.7)
|
||||
if (tmp.Length > 2 && tmp[1] == "whispers")
|
||||
string[] tmp = text.Split(' ');
|
||||
try
|
||||
{
|
||||
if (tmp.Length > 4 && tmp[2] == "to" && tmp[3] == "you:")
|
||||
//Detect vanilla /tell messages
|
||||
//Someone whispers message (MC 1.5)
|
||||
//Someone whispers to you: message (MC 1.7)
|
||||
if (tmp.Length > 2 && tmp[1] == "whispers")
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 18); //MC 1.7
|
||||
if (tmp.Length > 4 && tmp[2] == "to" && tmp[3] == "you:")
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 18); //MC 1.7
|
||||
}
|
||||
else message = text.Substring(tmp[0].Length + 10); //MC 1.5
|
||||
sender = tmp[0];
|
||||
return IsValidName(sender);
|
||||
}
|
||||
else message = text.Substring(tmp[0].Length + 10); //MC 1.5
|
||||
sender = tmp[0];
|
||||
return isValidName(sender);
|
||||
}
|
||||
|
||||
//Detect Essentials (Bukkit) /m messages
|
||||
//[Someone -> me] message
|
||||
//[~Someone -> me] message
|
||||
else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "->"
|
||||
&& (tmp[2] == "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);
|
||||
}
|
||||
//Detect Essentials (Bukkit) /m messages
|
||||
//[Someone -> me] message
|
||||
//[~Someone -> me] message
|
||||
else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "->"
|
||||
&& (tmp[2].ToLower() == "me]" || tmp[2].ToLower() == "moi]")) //'me' is replaced by 'moi' in french servers
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 1);
|
||||
sender = tmp[0].Substring(1);
|
||||
if (sender[0] == '~') { sender = sender.Substring(1); }
|
||||
return IsValidName(sender);
|
||||
}
|
||||
|
||||
//Detect Essentials (Bukkit) /me messages with some custom rank
|
||||
//[Someone [rank] -> me] message
|
||||
//[~Someone [rank] -> me] message
|
||||
else if (text[0] == '[' && tmp.Length > 3 && tmp[2] == "->"
|
||||
&& (tmp[3] == "me]" || tmp[3] == "moi]")) //'me' is replaced by 'moi' in french servers
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[2].Length + 1);
|
||||
sender = tmp[0].Substring(1);
|
||||
if (sender[0] == '~') { sender = sender.Substring(1); }
|
||||
return isValidName(sender);
|
||||
}
|
||||
//Detect Modified server messages. /m
|
||||
//[Someone @ me] message
|
||||
else if (text[0] == '[' && tmp.Length > 3 && tmp[1] == "@"
|
||||
&& (tmp[2].ToLower() == "me]" || tmp[2].ToLower() == "moi]")) //'me' is replaced by 'moi' in french servers
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 0);
|
||||
sender = tmp[0].Substring(1);
|
||||
if (sender[0] == '~') { sender = sender.Substring(1); }
|
||||
return IsValidName(sender);
|
||||
}
|
||||
|
||||
//Detect HeroChat PMsend
|
||||
//From Someone: message
|
||||
else if (text.StartsWith("From "))
|
||||
{
|
||||
sender = text.Substring(5).Split(':')[0];
|
||||
message = text.Substring(text.IndexOf(':') + 2);
|
||||
return isValidName(sender);
|
||||
}
|
||||
//Detect Essentials (Bukkit) /me messages with some custom prefix
|
||||
//[Prefix] [Someone -> me] message
|
||||
//[Prefix] [~Someone -> me] message
|
||||
else if (text[0] == '[' && tmp[0][tmp[0].Length - 1] == ']'
|
||||
&& tmp[1][0] == '[' && tmp.Length > 4 && tmp[2] == "->"
|
||||
&& (tmp[3].ToLower() == "me]" || tmp[3].ToLower() == "moi]"))
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[3].Length + 1);
|
||||
sender = tmp[1].Substring(1);
|
||||
if (sender[0] == '~') { sender = sender.Substring(1); }
|
||||
return IsValidName(sender);
|
||||
}
|
||||
|
||||
//Detect HeroChat Messages
|
||||
//[Channel] [Rank] User: Message
|
||||
else if (text.StartsWith("[") && text.Contains(':') && tmp.Length > 2)
|
||||
{
|
||||
int name_end = text.IndexOf(':');
|
||||
int name_start = text.Substring(0, name_end).LastIndexOf(']') + 2;
|
||||
sender = text.Substring(name_start, name_end - name_start);
|
||||
message = text.Substring(name_end + 2);
|
||||
return isValidName(sender);
|
||||
}
|
||||
//Detect Essentials (Bukkit) /me messages with some custom rank
|
||||
//[Someone [rank] -> me] message
|
||||
//[~Someone [rank] -> me] message
|
||||
else if (text[0] == '[' && tmp.Length > 3 && tmp[2] == "->"
|
||||
&& (tmp[3].ToLower() == "me]" || tmp[3].ToLower() == "moi]"))
|
||||
{
|
||||
message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[2].Length + 1);
|
||||
sender = tmp[0].Substring(1);
|
||||
if (sender[0] == '~') { sender = sender.Substring(1); }
|
||||
return IsValidName(sender);
|
||||
}
|
||||
|
||||
else return false;
|
||||
//Detect HeroChat PMsend
|
||||
//From Someone: message
|
||||
else if (text.StartsWith("From "))
|
||||
{
|
||||
sender = text.Substring(5).Split(':')[0];
|
||||
message = text.Substring(text.IndexOf(':') + 2);
|
||||
return IsValidName(sender);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
catch (IndexOutOfRangeException) { /* Not an expected chat format */ }
|
||||
catch (ArgumentOutOfRangeException) { /* Same here */ }
|
||||
}
|
||||
catch (IndexOutOfRangeException) { return false; }
|
||||
|
||||
//User-defined regex for private chat messages
|
||||
if (Settings.ChatFormat_Private != null)
|
||||
{
|
||||
Match regexMatch = Settings.ChatFormat_Private.Match(text);
|
||||
if (regexMatch.Success && regexMatch.Groups.Count >= 3)
|
||||
{
|
||||
sender = regexMatch.Groups[1].Value;
|
||||
message = regexMatch.Groups[2].Value;
|
||||
return IsValidName(sender);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -237,33 +339,101 @@ namespace MinecraftClient
|
|||
/// <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)
|
||||
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
|
||||
text = getVerbatim(text);
|
||||
if (text == "") { return false; }
|
||||
if (text[0] == '<')
|
||||
if (String.IsNullOrEmpty(text))
|
||||
return false;
|
||||
|
||||
text = GetVerbatim(text);
|
||||
|
||||
//Built-in detection routine for public messages
|
||||
if (Settings.ChatFormat_Builtins)
|
||||
{
|
||||
try
|
||||
string[] tmp = text.Split(' ');
|
||||
|
||||
//Detect vanilla/factions Messages
|
||||
//<Someone> message
|
||||
//<*Faction Someone> message
|
||||
//<*Faction Someone>: message
|
||||
//<*Faction ~Nicknamed>: message
|
||||
if (text[0] == '<')
|
||||
{
|
||||
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);
|
||||
try
|
||||
{
|
||||
text = text.Substring(1);
|
||||
string[] tmp2 = text.Split('>');
|
||||
sender = tmp2[0];
|
||||
message = text.Substring(sender.Length + 2);
|
||||
if (message.Length > 1 && message[0] == ' ')
|
||||
{ message = message.Substring(1); }
|
||||
tmp2 = sender.Split(' ');
|
||||
sender = tmp2[tmp2.Length - 1];
|
||||
if (sender[0] == '~') { sender = sender.Substring(1); }
|
||||
return IsValidName(sender);
|
||||
}
|
||||
catch (IndexOutOfRangeException) { /* Not a vanilla/faction message */ }
|
||||
catch (ArgumentOutOfRangeException) { /* Same here */ }
|
||||
}
|
||||
|
||||
//Detect HeroChat Messages
|
||||
//Public chat messages
|
||||
//[Channel] [Rank] User: Message
|
||||
else if (text[0] == '[' && text.Contains(':') && tmp.Length > 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
int name_end = text.IndexOf(':');
|
||||
int name_start = text.Substring(0, name_end).LastIndexOf(']') + 2;
|
||||
sender = text.Substring(name_start, name_end - name_start);
|
||||
message = text.Substring(name_end + 2);
|
||||
return IsValidName(sender);
|
||||
}
|
||||
catch (IndexOutOfRangeException) { /* Not a herochat message */ }
|
||||
catch (ArgumentOutOfRangeException) { /* Same here */ }
|
||||
}
|
||||
|
||||
//Detect (Unknown Plugin) Messages
|
||||
//**Faction<Rank> User : Message
|
||||
else if (text[0] == '*'
|
||||
&& text.Length > 1
|
||||
&& text[1] != ' '
|
||||
&& text.Contains('<') && text.Contains('>')
|
||||
&& text.Contains(' ') && text.Contains(':')
|
||||
&& text.IndexOf('*') < text.IndexOf('<')
|
||||
&& text.IndexOf('<') < text.IndexOf('>')
|
||||
&& text.IndexOf('>') < text.IndexOf(' ')
|
||||
&& text.IndexOf(' ') < text.IndexOf(':'))
|
||||
{
|
||||
try
|
||||
{
|
||||
string prefix = tmp[0];
|
||||
string user = tmp[1];
|
||||
string semicolon = tmp[2];
|
||||
if (prefix.All(c => char.IsLetterOrDigit(c) || new char[] { '*', '<', '>', '_' }.Contains(c))
|
||||
&& semicolon == ":")
|
||||
{
|
||||
message = text.Substring(prefix.Length + user.Length + 4);
|
||||
return IsValidName(user);
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfRangeException) { /* Not a <unknown plugin> message */ }
|
||||
catch (ArgumentOutOfRangeException) { /* Same here */ }
|
||||
}
|
||||
catch (IndexOutOfRangeException) { return false; }
|
||||
}
|
||||
else return false;
|
||||
|
||||
//User-defined regex for public chat messages
|
||||
if (Settings.ChatFormat_Public != null)
|
||||
{
|
||||
Match regexMatch = Settings.ChatFormat_Public.Match(text);
|
||||
if (regexMatch.Success && regexMatch.Groups.Count >= 3)
|
||||
{
|
||||
sender = regexMatch.Groups[1].Value;
|
||||
message = regexMatch.Groups[2].Value;
|
||||
return IsValidName(sender);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -273,27 +443,65 @@ namespace MinecraftClient
|
|||
/// <param name="sender">Will contain the sender's username, if it's a teleport request</param>
|
||||
/// <returns>Returns true if the text is a teleport request</returns>
|
||||
|
||||
protected static bool isTeleportRequest(string text, ref string sender)
|
||||
protected static bool IsTeleportRequest(string text, ref string sender)
|
||||
{
|
||||
text = getVerbatim(text);
|
||||
sender = text.Split(' ')[0];
|
||||
if (text.EndsWith("has requested to teleport to you.")
|
||||
|| text.EndsWith("has requested that you teleport to them."))
|
||||
if (String.IsNullOrEmpty(text))
|
||||
return false;
|
||||
|
||||
text = GetVerbatim(text);
|
||||
|
||||
//Built-in detection routine for teleport requests
|
||||
if (Settings.ChatFormat_Builtins)
|
||||
{
|
||||
return isValidName(sender);
|
||||
string[] tmp = text.Split(' ');
|
||||
|
||||
//Detect Essentials teleport requests, prossibly with
|
||||
//nicknamed names or other modifications such as HeroChat
|
||||
if (text.EndsWith("has requested to teleport to you.")
|
||||
|| text.EndsWith("has requested that you teleport to them."))
|
||||
{
|
||||
//<Rank> Username has requested...
|
||||
//[Rank] Username has requested...
|
||||
if (((tmp[0].StartsWith("<") && tmp[0].EndsWith(">"))
|
||||
|| (tmp[0].StartsWith("[") && tmp[0].EndsWith("]")))
|
||||
&& tmp.Length > 1)
|
||||
sender = tmp[1];
|
||||
|
||||
//Username has requested...
|
||||
else sender = tmp[0];
|
||||
|
||||
//~Username has requested...
|
||||
if (sender.Length > 1 && sender[0] == '~')
|
||||
sender = sender.Substring(1);
|
||||
|
||||
//Final check on username validity
|
||||
return IsValidName(sender);
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
|
||||
//User-defined regex for teleport requests
|
||||
if (Settings.ChatFormat_TeleportRequest != null)
|
||||
{
|
||||
Match regexMatch = Settings.ChatFormat_TeleportRequest.Match(text);
|
||||
if (regexMatch.Success && regexMatch.Groups.Count >= 2)
|
||||
{
|
||||
sender = regexMatch.Groups[1].Value;
|
||||
return IsValidName(sender);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes some text in the console. Nothing will be sent to the server.
|
||||
/// Write 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)
|
||||
protected void LogToConsole(object text)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8[BOT] " + text);
|
||||
string logfile = Settings.expandVars(Settings.chatbotLogFile);
|
||||
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", this.GetType().Name, text));
|
||||
string logfile = Settings.ExpandVars(Settings.chatbotLogFile);
|
||||
|
||||
if (!String.IsNullOrEmpty(logfile))
|
||||
{
|
||||
|
|
@ -305,26 +513,19 @@ namespace MinecraftClient
|
|||
catch { return; /* Invalid file name or access denied */ }
|
||||
}
|
||||
|
||||
File.AppendAllLines(logfile, new string[] { getTimestamp() + ' ' + text });
|
||||
File.AppendAllLines(logfile, new string[] { GetTimestamp() + ' ' + text });
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// It will unload and 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)
|
||||
protected void ReconnectToTheServer(int ExtraAttempts = 3)
|
||||
{
|
||||
McTcpClient.AttemptsLeft = ExtraAttempts;
|
||||
McTcpClient.ReconnectionAttemptsLeft = ExtraAttempts;
|
||||
Program.Restart();
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +544,7 @@ namespace MinecraftClient
|
|||
|
||||
protected void UnloadBot()
|
||||
{
|
||||
handler.BotUnLoad(this);
|
||||
Handler.BotUnLoad(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -354,7 +555,7 @@ namespace MinecraftClient
|
|||
|
||||
protected void SendPrivateMessage(string player, string message)
|
||||
{
|
||||
SendText("/tell " + player + ' ' + message);
|
||||
SendText(String.Format("/{0} {1} {2}", Settings.PrivateMsgsCmdName, player, message));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -365,26 +566,102 @@ namespace MinecraftClient
|
|||
|
||||
protected void RunScript(string filename, string playername = "")
|
||||
{
|
||||
handler.BotLoad(new ChatBots.Script(filename, playername));
|
||||
Handler.BotLoad(new ChatBots.Script(filename, playername));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a D-M-Y h:m:s timestamp representing the current system date and time
|
||||
/// Get the current Minecraft World
|
||||
/// </summary>
|
||||
/// <returns>Minecraft world or null if associated setting is disabled</returns>
|
||||
|
||||
protected Mapping.World GetWorld()
|
||||
{
|
||||
if (Settings.TerrainAndMovements)
|
||||
return Handler.GetWorld();
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a Y-M-D h:m:s timestamp representing the current system date and time
|
||||
/// </summary>
|
||||
|
||||
protected static string getTimestamp()
|
||||
protected static string GetTimestamp()
|
||||
{
|
||||
DateTime time = DateTime.Now;
|
||||
return String.Format("{0}-{1}-{2} {3}:{4}:{5}",
|
||||
time.Year.ToString("0000"),
|
||||
time.Month.ToString("00"),
|
||||
time.Day.ToString("00"),
|
||||
time.Hour.ToString("00"),
|
||||
time.Minute.ToString("00"),
|
||||
time.Second.ToString("00"));
|
||||
}
|
||||
|
||||
string D = time.Day.ToString("00");
|
||||
string M = time.Month.ToString("00");
|
||||
string Y = time.Year.ToString("0000");
|
||||
/// <summary>
|
||||
/// Load entries from a file as a string array, removing duplicates and empty lines
|
||||
/// </summary>
|
||||
/// <param name="file">File to load</param>
|
||||
/// <returns>The string array or an empty array if failed to load the file</returns>
|
||||
|
||||
protected string[] LoadDistinctEntriesFromFile(string file)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
//Read all lines from file, remove lines with no text, convert to lowercase,
|
||||
//remove duplicate entries, convert to a string array, and return the result.
|
||||
return File.ReadAllLines(file)
|
||||
.Where(line => !String.IsNullOrWhiteSpace(line))
|
||||
.Select(line => line.ToLower())
|
||||
.Distinct().ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogToConsole("File not found: " + Settings.Alerts_MatchesFile);
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
string h = time.Hour.ToString("00");
|
||||
string m = time.Minute.ToString("00");
|
||||
string s = time.Second.ToString("00");
|
||||
/// <summary>
|
||||
/// Registers the given plugin channel for use by this chatbot.
|
||||
/// </summary>
|
||||
/// <param name="channel">The name of the channel to register</param>
|
||||
|
||||
return "" + D + '-' + M + '-' + Y + ' ' + h + ':' + m + ':' + s;
|
||||
protected void RegisterPluginChannel(string channel)
|
||||
{
|
||||
this.registeredPluginChannels.Add(channel);
|
||||
Handler.RegisterPluginChannel(channel, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the given plugin channel, meaning this chatbot can no longer use it.
|
||||
/// </summary>
|
||||
/// <param name="channel">The name of the channel to unregister</param>
|
||||
|
||||
protected void UnregisterPluginChannel(string channel)
|
||||
{
|
||||
this.registeredPluginChannels.RemoveAll(chan => chan == channel);
|
||||
Handler.UnregisterPluginChannel(channel, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the given plugin channel message to the server, if the channel has been registered.
|
||||
/// See http://wiki.vg/Plugin_channel for more information about plugin channels.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to send the message on.</param>
|
||||
/// <param name="data">The data to send.</param>
|
||||
/// <param name="sendEvenIfNotRegistered">Should the message be sent even if it hasn't been registered by the server or this bot? (Some Minecraft channels aren't registered)</param>
|
||||
/// <returns>Whether the message was successfully sent. False if there was a network error or if the channel wasn't registered.</returns>
|
||||
|
||||
protected bool SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false)
|
||||
{
|
||||
if (!sendEvenIfNotRegistered)
|
||||
{
|
||||
if (!this.registeredPluginChannels.Contains(channel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Handler.SendPluginChannelMessage(channel, data, sendEvenIfNotRegistered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,36 +14,13 @@ namespace MinecraftClient.ChatBots
|
|||
private string[] dictionary = new string[0];
|
||||
private string[] excludelist = new string[0];
|
||||
|
||||
/// <summary>
|
||||
/// Import alerts from the specified file
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
private static string[] FromFile(string file)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
//Read all lines from file, remove lines with no text, convert to lowercase,
|
||||
//remove duplicate entries, convert to a string array, and return the result.
|
||||
return File.ReadAllLines(file)
|
||||
.Where(line => !String.IsNullOrWhiteSpace(line))
|
||||
.Select(line => line.ToLower())
|
||||
.Distinct().ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogToConsole("File not found: " + Settings.Alerts_MatchesFile);
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intitialize the Alerts bot
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
dictionary = FromFile(Settings.Alerts_MatchesFile);
|
||||
excludelist = FromFile(Settings.Alerts_ExcludesFile);
|
||||
dictionary = LoadDistinctEntriesFromFile(Settings.Alerts_MatchesFile);
|
||||
excludelist = LoadDistinctEntriesFromFile(Settings.Alerts_ExcludesFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -53,7 +30,7 @@ namespace MinecraftClient.ChatBots
|
|||
public override void GetText(string text)
|
||||
{
|
||||
//Remove color codes and convert to lowercase
|
||||
text = getVerbatim(text).ToLower();
|
||||
text = GetVerbatim(text).ToLower();
|
||||
|
||||
//Proceed only if no exclusions are found in text
|
||||
if (!excludelist.Any(exclusion => text.Contains(exclusion)))
|
||||
|
|
@ -67,10 +44,13 @@ namespace MinecraftClient.ChatBots
|
|||
if (ConsoleIO.basicIO) //Using a GUI? Pass text as is.
|
||||
ConsoleIO.WriteLine(text.Replace(alert, "§c" + alert + "§r"));
|
||||
|
||||
else //Using Consome Prompt : Print text with alert highlighted
|
||||
else //Using Console Prompt : Print text with alert highlighted
|
||||
{
|
||||
string[] splitted = text.Split(new string[] { alert }, StringSplitOptions.None);
|
||||
|
||||
ConsoleColor fore = Console.ForegroundColor;
|
||||
ConsoleColor back = Console.BackgroundColor;
|
||||
|
||||
if (splitted.Length > 0)
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.DarkGray;
|
||||
|
|
@ -89,8 +69,8 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
}
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.BackgroundColor = back;
|
||||
Console.ForegroundColor = fore;
|
||||
ConsoleIO.Write('\n');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
attempts = retries;
|
||||
if (attempts == -1) { attempts = int.MaxValue; }
|
||||
McTcpClient.AttemptsLeft = attempts;
|
||||
McTcpClient.ReconnectionAttemptsLeft = attempts;
|
||||
delay = DelayBeforeRelog;
|
||||
if (delay < 1) { delay = 1; }
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
McTcpClient.AttemptsLeft = attempts;
|
||||
McTcpClient.ReconnectionAttemptsLeft = attempts;
|
||||
if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile))
|
||||
{
|
||||
dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile);
|
||||
|
|
@ -47,7 +47,7 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override bool OnDisconnect(DisconnectReason reason, string message)
|
||||
{
|
||||
message = getVerbatim(message);
|
||||
message = GetVerbatim(message);
|
||||
string comp = message.ToLower();
|
||||
foreach (string msg in dictionary)
|
||||
{
|
||||
|
|
@ -55,12 +55,22 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
LogToConsole("Waiting " + delay + " seconds before reconnecting...");
|
||||
System.Threading.Thread.Sleep(delay * 1000);
|
||||
McTcpClient.AttemptsLeft = attempts;
|
||||
McTcpClient.ReconnectionAttemptsLeft = attempts;
|
||||
ReconnectToTheServer();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool OnDisconnectStatic(DisconnectReason reason, string message)
|
||||
{
|
||||
if (Settings.AutoRelog_Enabled)
|
||||
{
|
||||
AutoRelog bot = new AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries);
|
||||
return bot.OnDisconnect(reason, message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
226
MinecraftClient/ChatBots/AutoRespond.cs
Normal file
226
MinecraftClient/ChatBots/AutoRespond.cs
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
/// <summary>
|
||||
/// This bot automatically runs actions when a user sends a message matching a specified rule
|
||||
/// </summary>
|
||||
class AutoRespond : ChatBot
|
||||
{
|
||||
private string matchesFile;
|
||||
private List<RespondRule> respondRules;
|
||||
private enum MessageType { Public, Private, Other };
|
||||
|
||||
/// <summary>
|
||||
/// Create a new AutoRespond bot
|
||||
/// </summary>
|
||||
/// <param name="matchesFile">INI File to load matches from</param>
|
||||
public AutoRespond(string matchesFile)
|
||||
{
|
||||
this.matchesFile = matchesFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describe a respond rule based on a simple match or a regex
|
||||
/// </summary>
|
||||
private class RespondRule
|
||||
{
|
||||
private Regex regex;
|
||||
private string match;
|
||||
private string actionPublic;
|
||||
private string actionPrivate;
|
||||
private string actionOther;
|
||||
|
||||
/// <summary>
|
||||
/// Create a respond rule from a regex and a reponse message or command
|
||||
/// </summary>
|
||||
/// <param name="regex">Regex</param>
|
||||
/// <param name="actionPublic">Internal command to run for public messages</param>
|
||||
/// <param name="actionPrivate">Internal command to run for private messages</param>
|
||||
/// <param name="actionOther">Internal command to run for any other messages</param>
|
||||
public RespondRule(Regex regex, string actionPublic, string actionPrivate, string actionOther)
|
||||
{
|
||||
this.regex = regex;
|
||||
this.match = null;
|
||||
this.actionPublic = actionPublic;
|
||||
this.actionPrivate = actionPrivate;
|
||||
this.actionOther = actionOther;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a respond rule from a match string and a reponse message or command
|
||||
/// </summary>
|
||||
/// <param name="match">Match string</param>
|
||||
/// <param name="actionPublic">Internal command to run for public messages</param>
|
||||
/// <param name="actionPrivate">Internal command to run for private messages</param>
|
||||
public RespondRule(string match, string actionPublic, string actionPrivate, string actionOther)
|
||||
{
|
||||
this.regex = null;
|
||||
this.match = match;
|
||||
this.actionPublic = actionPublic;
|
||||
this.actionPrivate = actionPrivate;
|
||||
this.actionOther = actionOther;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Match the respond rule to the specified string and return a message or command to send if a match is detected
|
||||
/// </summary>
|
||||
/// <param name="username">Player who have sent the message</param>
|
||||
/// <param name="message">Message to match against the regex or match string</param>
|
||||
/// <param name="msgType">Type of the message public/private message, or other message</param>
|
||||
/// <returns>Internal command to run as a response to this user, or null if no match has been detected</returns>
|
||||
public string Match(string username, string message, MessageType msgType)
|
||||
{
|
||||
string toSend = null;
|
||||
|
||||
switch (msgType)
|
||||
{
|
||||
case MessageType.Public: toSend = actionPublic; break;
|
||||
case MessageType.Private: toSend = actionPrivate; break;
|
||||
case MessageType.Other: toSend = actionOther; break;
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(toSend))
|
||||
return null;
|
||||
|
||||
if (regex != null)
|
||||
{
|
||||
if (regex.IsMatch(message))
|
||||
{
|
||||
Match regexMatch = regex.Match(message);
|
||||
for (int i = regexMatch.Groups.Count - 1; i >= 1; i--)
|
||||
toSend = toSend.Replace("$" + i, regexMatch.Groups[i].Value);
|
||||
toSend = toSend.Replace("$u", username);
|
||||
return toSend;
|
||||
}
|
||||
}
|
||||
else if (!String.IsNullOrEmpty(match))
|
||||
{
|
||||
if (message.ToLower().Contains(match.ToLower()))
|
||||
{
|
||||
return toSend.Replace("$u", username);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the AutoRespond bot from the matches file
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
if (File.Exists(matchesFile))
|
||||
{
|
||||
Regex matchRegex = null;
|
||||
string matchString = null;
|
||||
string matchAction = null;
|
||||
string matchActionPrivate = null;
|
||||
string matchActionOther = null;
|
||||
respondRules = new List<RespondRule>();
|
||||
|
||||
foreach (string lineRAW in File.ReadAllLines(matchesFile))
|
||||
{
|
||||
string line = lineRAW.Split('#')[0].Trim();
|
||||
if (line.Length > 0)
|
||||
{
|
||||
if (line[0] == '[' && line[line.Length - 1] == ']')
|
||||
{
|
||||
switch (line.Substring(1, line.Length - 2).ToLower())
|
||||
{
|
||||
case "match":
|
||||
CheckAddMatch(matchRegex, matchString, matchAction, matchActionPrivate, matchActionOther);
|
||||
matchRegex = null;
|
||||
matchString = null;
|
||||
matchAction = null;
|
||||
matchActionPrivate = null;
|
||||
matchActionOther = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string argName = line.Split('=')[0];
|
||||
if (line.Length > (argName.Length + 1))
|
||||
{
|
||||
string argValue = line.Substring(argName.Length + 1);
|
||||
switch (argName.ToLower())
|
||||
{
|
||||
case "regex": matchRegex = new Regex(argValue); break;
|
||||
case "match": matchString = argValue; break;
|
||||
case "action": matchAction = argValue; break;
|
||||
case "actionprivate": matchActionPrivate = argValue; break;
|
||||
case "actionother": matchActionOther = argValue; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckAddMatch(matchRegex, matchString, matchAction, matchActionPrivate, matchActionOther);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogToConsole("File not found: '" + matchesFile + "'");
|
||||
UnloadBot(); //No need to keep the bot active
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new respond rule from the provided arguments, only if they are valid: at least one match and one action
|
||||
/// </summary>
|
||||
/// <param name="matchRegex">Matching regex</param>
|
||||
/// <param name="matchString">Matching string</param>
|
||||
/// <param name="matchAction">Action if the matching message is public</param>
|
||||
/// <param name="matchActionPrivate">Action if the matching message is private</param>
|
||||
private void CheckAddMatch(Regex matchRegex, string matchString, string matchAction, string matchActionPrivate, string matchActionOther)
|
||||
{
|
||||
if (matchAction != null || matchActionPrivate != null || matchActionOther != null)
|
||||
{
|
||||
if (matchRegex != null)
|
||||
{
|
||||
respondRules.Add(new RespondRule(matchRegex, matchAction, matchActionPrivate, matchActionOther));
|
||||
}
|
||||
else if (matchString != null)
|
||||
{
|
||||
respondRules.Add(new RespondRule(matchString, matchAction, matchActionPrivate, matchActionOther));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void GetText(string text)
|
||||
{
|
||||
//Remove colour codes
|
||||
text = GetVerbatim(text);
|
||||
|
||||
//Get Message type
|
||||
string sender = "", message = "";
|
||||
MessageType msgType = MessageType.Other;
|
||||
if (IsChatMessage(text, ref message, ref sender))
|
||||
msgType = MessageType.Public;
|
||||
else if (IsPrivateMessage(text, ref message, ref sender))
|
||||
msgType = MessageType.Private;
|
||||
else message = text;
|
||||
|
||||
//Do not process messages sent by the bot itself
|
||||
if (msgType == MessageType.Other || sender != Settings.Username)
|
||||
{
|
||||
foreach (RespondRule rule in respondRules)
|
||||
{
|
||||
string toPerform = rule.Match(sender, message, msgType);
|
||||
if (!String.IsNullOrEmpty(toPerform))
|
||||
{
|
||||
string response = null;
|
||||
LogToConsole(toPerform);
|
||||
PerformInternalCommand(toPerform, ref response);
|
||||
if (!String.IsNullOrEmpty(response))
|
||||
LogToConsole(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,15 +69,15 @@ namespace MinecraftClient.ChatBots
|
|||
|
||||
public override void GetText(string text)
|
||||
{
|
||||
text = getVerbatim(text);
|
||||
text = GetVerbatim(text);
|
||||
string sender = "";
|
||||
string message = "";
|
||||
|
||||
if (saveChat && isChatMessage(text, ref message, ref sender))
|
||||
if (saveChat && IsChatMessage(text, ref message, ref sender))
|
||||
{
|
||||
save("Chat " + sender + ": " + message);
|
||||
}
|
||||
else if (savePrivate && isPrivateMessage(text, ref message, ref sender))
|
||||
else if (savePrivate && IsPrivateMessage(text, ref message, ref sender))
|
||||
{
|
||||
save("Private " + sender + ": " + message);
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ namespace MinecraftClient.ChatBots
|
|||
private void save(string tosave)
|
||||
{
|
||||
if (dateandtime)
|
||||
tosave = getTimestamp() + ' ' + tosave;
|
||||
tosave = GetTimestamp() + ' ' + tosave;
|
||||
|
||||
string directory = Path.GetDirectoryName(logfile);
|
||||
if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
string message = "";
|
||||
string username = "";
|
||||
text = getVerbatim(text);
|
||||
text = GetVerbatim(text);
|
||||
|
||||
if (isPrivateMessage(text, ref message, ref username))
|
||||
if (IsPrivateMessage(text, ref message, ref username))
|
||||
{
|
||||
if (Settings.Bots_Owners.Contains(username.ToLower()))
|
||||
{
|
||||
|
|
@ -73,7 +73,7 @@ namespace MinecraftClient.ChatBots
|
|||
}
|
||||
else
|
||||
{
|
||||
if (running && isChatMessage(text, ref message, ref username))
|
||||
if (running && IsChatMessage(text, ref message, ref username))
|
||||
{
|
||||
if (message.Length == 1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ namespace MinecraftClient.ChatBots
|
|||
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");
|
||||
System.IO.File.AppendAllText(file, TimeStamp + "\n" + GetVerbatim(text) + "\n\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,19 +13,19 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
public override void GetText(string text)
|
||||
{
|
||||
text = getVerbatim(text);
|
||||
text = GetVerbatim(text);
|
||||
string command = "", sender = "";
|
||||
if (isPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim()))
|
||||
if (IsPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim()))
|
||||
{
|
||||
string response = "";
|
||||
performInternalCommand(command, ref response);
|
||||
PerformInternalCommand(command, ref response);
|
||||
if (response.Length > 0)
|
||||
{
|
||||
SendPrivateMessage(sender, response);
|
||||
}
|
||||
}
|
||||
else if (Settings.RemoteCtrl_AutoTpaccept
|
||||
&& isTeleportRequest(text, ref sender)
|
||||
&& IsTeleportRequest(text, ref sender)
|
||||
&& (Settings.RemoteCtrl_AutoTpaccept_Everyone || Settings.Bots_Owners.Contains(sender.ToLower().Trim())))
|
||||
{
|
||||
SendText("/tpaccept");
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.CSharp;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MinecraftClient.ChatBots
|
||||
{
|
||||
|
|
@ -13,14 +17,17 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
private string file;
|
||||
private string[] lines = new string[0];
|
||||
private string[] args = new string[0];
|
||||
private int sleepticks = 10;
|
||||
private int sleepticks_interval = 10;
|
||||
private int nextline = 0;
|
||||
private string owner;
|
||||
private bool csharp;
|
||||
private Thread thread;
|
||||
private ManualResetEvent tpause;
|
||||
|
||||
public Script(string filename)
|
||||
{
|
||||
file = filename;
|
||||
ParseArguments(filename);
|
||||
}
|
||||
|
||||
public Script(string filename, string ownername)
|
||||
|
|
@ -30,7 +37,52 @@ namespace MinecraftClient.ChatBots
|
|||
owner = ownername;
|
||||
}
|
||||
|
||||
public static bool lookForScript(ref string filename)
|
||||
private void ParseArguments(string argstr)
|
||||
{
|
||||
List<string> args = new List<string>();
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
bool escape = false;
|
||||
bool quotes = false;
|
||||
|
||||
foreach (char c in argstr)
|
||||
{
|
||||
if (escape)
|
||||
{
|
||||
if (c != '"')
|
||||
str.Append('\\');
|
||||
str.Append(c);
|
||||
escape = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '\\')
|
||||
escape = true;
|
||||
else if (c == '"')
|
||||
quotes = !quotes;
|
||||
else if (c == ' ' && !quotes)
|
||||
{
|
||||
if (str.Length > 0)
|
||||
args.Add(str.ToString());
|
||||
str.Clear();
|
||||
}
|
||||
else str.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (str.Length > 0)
|
||||
args.Add(str.ToString());
|
||||
|
||||
if (args.Count > 0)
|
||||
{
|
||||
file = args[0];
|
||||
args.RemoveAt(0);
|
||||
this.args = args.ToArray();
|
||||
}
|
||||
else file = "";
|
||||
}
|
||||
|
||||
public static bool LookForScript(ref string filename)
|
||||
{
|
||||
//Automatically look in subfolders and try to add ".txt" file extension
|
||||
char dir_slash = Program.isUsingMono ? '/' : '\\';
|
||||
|
|
@ -38,10 +90,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
filename,
|
||||
filename + ".txt",
|
||||
filename + ".cs",
|
||||
"scripts" + dir_slash + filename,
|
||||
"scripts" + dir_slash + filename + ".txt",
|
||||
"scripts" + dir_slash + filename + ".cs",
|
||||
"config" + dir_slash + filename,
|
||||
"config" + dir_slash + filename + ".txt",
|
||||
"config" + dir_slash + filename + ".cs",
|
||||
};
|
||||
|
||||
foreach (string possible_file in files)
|
||||
|
|
@ -59,64 +114,105 @@ namespace MinecraftClient.ChatBots
|
|||
public override void Initialize()
|
||||
{
|
||||
//Load the given file from the startup parameters
|
||||
if (lookForScript(ref file))
|
||||
if (LookForScript(ref file))
|
||||
{
|
||||
lines = System.IO.File.ReadAllLines(file);
|
||||
if (owner != null) { SendPrivateMessage(owner, "Script '" + file + "' loaded."); }
|
||||
csharp = file.EndsWith(".cs");
|
||||
thread = null;
|
||||
|
||||
if (owner != null)
|
||||
SendPrivateMessage(owner, "Script '" + file + "' loaded.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogToConsole("File not found: '" + file + "'");
|
||||
|
||||
if (owner != null)
|
||||
SendPrivateMessage(owner, "File not found: '" + file + "'");
|
||||
|
||||
UnloadBot(); //No need to keep the bot active
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (sleepticks > 0) { sleepticks--; }
|
||||
else
|
||||
if (csharp) //C# compiled script
|
||||
{
|
||||
if (nextline < lines.Length) //Is there an instruction left to interpret?
|
||||
//Initialize thread on first update
|
||||
if (thread == null)
|
||||
{
|
||||
string instruction_line = lines[nextline].Trim(); // Removes all whitespaces at start and end of current line
|
||||
nextline++; //Move the cursor so that the next time the following line will be interpreted
|
||||
sleepticks = sleepticks_interval; //Used to delay next command sending and prevent from beign kicked for spamming
|
||||
|
||||
if (instruction_line.Length > 1)
|
||||
tpause = new ManualResetEvent(false);
|
||||
thread = new Thread(() =>
|
||||
{
|
||||
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
|
||||
try
|
||||
{
|
||||
instruction_line = Settings.expandVars(instruction_line);
|
||||
string instruction_name = instruction_line.Split(' ')[0];
|
||||
switch (instruction_name.ToLower())
|
||||
{
|
||||
case "wait":
|
||||
int ticks = 10;
|
||||
try
|
||||
{
|
||||
ticks = Convert.ToInt32(instruction_line.Substring(5, instruction_line.Length - 5));
|
||||
}
|
||||
catch { }
|
||||
sleepticks = ticks;
|
||||
break;
|
||||
default:
|
||||
if (!performInternalCommand(instruction_line))
|
||||
{
|
||||
sleepticks = 0; Update(); //Unknown command : process next line immediately
|
||||
}
|
||||
else if (instruction_name.ToLower() != "log") { LogToConsole(instruction_line); }
|
||||
break;
|
||||
}
|
||||
CSharpRunner.Run(this, tpause, lines, args);
|
||||
}
|
||||
else { sleepticks = 0; Update(); } //Comment: process next line immediately
|
||||
}
|
||||
catch (CSharpException e)
|
||||
{
|
||||
string errorMessage = "Script '" + file + "' failed to run (" + e.ExceptionType + ").";
|
||||
LogToConsole(errorMessage);
|
||||
if (owner != null)
|
||||
SendPrivateMessage(owner, errorMessage);
|
||||
LogToConsole(e.InnerException);
|
||||
}
|
||||
});
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
//Let the thread run for a short span of time
|
||||
if (thread != null)
|
||||
{
|
||||
tpause.Set();
|
||||
tpause.Reset();
|
||||
if (!thread.IsAlive)
|
||||
UnloadBot();
|
||||
}
|
||||
}
|
||||
else //Classic MCC script interpreter
|
||||
{
|
||||
if (sleepticks > 0) { sleepticks--; }
|
||||
else
|
||||
{
|
||||
//No more instructions to interpret
|
||||
UnloadBot();
|
||||
if (nextline < lines.Length) //Is there an instruction left to interpret?
|
||||
{
|
||||
string instruction_line = lines[nextline].Trim(); // Removes all whitespaces at start and end of current line
|
||||
nextline++; //Move the cursor so that the next time the following line will be interpreted
|
||||
|
||||
if (instruction_line.Length > 1)
|
||||
{
|
||||
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
|
||||
{
|
||||
instruction_line = Settings.ExpandVars(instruction_line);
|
||||
string instruction_name = instruction_line.Split(' ')[0];
|
||||
switch (instruction_name.ToLower())
|
||||
{
|
||||
case "wait":
|
||||
int ticks = 10;
|
||||
try
|
||||
{
|
||||
ticks = Convert.ToInt32(instruction_line.Substring(5, instruction_line.Length - 5));
|
||||
}
|
||||
catch { }
|
||||
sleepticks = ticks;
|
||||
break;
|
||||
default:
|
||||
if (!PerformInternalCommand(instruction_line))
|
||||
{
|
||||
Update(); //Unknown command : process next line immediately
|
||||
}
|
||||
else if (instruction_name.ToLower() != "log") { LogToConsole(instruction_line); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
else { Update(); } //Comment: process next line immediately
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//No more instructions to interpret
|
||||
UnloadBot();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ namespace MinecraftClient.ChatBots
|
|||
if (current_task != null)
|
||||
{
|
||||
//Check if we built a valid task before adding it
|
||||
if (current_task.script_file != null && Script.lookForScript(ref current_task.script_file) //Check if file exists
|
||||
if (current_task.script_file != null && Script.LookForScript(ref current_task.script_file) //Check if file exists
|
||||
&& (current_task.triggerOnLogin
|
||||
|| (current_task.triggerOnTime && current_task.triggerOnTime_Times.Count > 0))
|
||||
|| (current_task.triggerOnInterval && current_task.triggerOnInterval_Interval > 0)) //Look for a valid trigger
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ namespace MinecraftClient.ChatBots
|
|||
{
|
||||
string message = "";
|
||||
string username = "";
|
||||
text = getVerbatim(text);
|
||||
text = GetVerbatim(text);
|
||||
|
||||
if (isPrivateMessage(text, ref message, ref username))
|
||||
if (IsPrivateMessage(text, ref message, ref username))
|
||||
{
|
||||
ConsoleIO.WriteLine("Bot: " + username + " told me : " + message);
|
||||
}
|
||||
else if (isChatMessage(text, ref message, ref username))
|
||||
else if (IsChatMessage(text, ref message, ref username))
|
||||
{
|
||||
ConsoleIO.WriteLine("Bot: " + username + " said : " + message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ namespace MinecraftClient.Commands
|
|||
string[] args = getArgs(command);
|
||||
if (args.Length > 1)
|
||||
{
|
||||
if (!Settings.setAccount(args[1]))
|
||||
if (!Settings.SetAccount(args[1]))
|
||||
{
|
||||
return "Unknown account '" + args[1] + "'.";
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.setServerIP(args[0]))
|
||||
if (Settings.SetServerIP(args[0]))
|
||||
{
|
||||
Program.Restart();
|
||||
return "";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace MinecraftClient.Commands
|
|||
|
||||
public override string Run(McTcpClient handler, string command)
|
||||
{
|
||||
return "PlayerList: " + String.Join(", ", handler.getOnlinePlayers());
|
||||
return "PlayerList: " + String.Join(", ", handler.GetOnlinePlayers());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace MinecraftClient.Commands
|
|||
{
|
||||
if (hasArg(command))
|
||||
{
|
||||
ChatBot.LogToConsole(getArg(command));
|
||||
ConsoleIO.WriteLogLine(getArg(command));
|
||||
return "";
|
||||
}
|
||||
else return CMDDesc;
|
||||
|
|
|
|||
60
MinecraftClient/Commands/Move.cs
Normal file
60
MinecraftClient/Commands/Move.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient.Commands
|
||||
{
|
||||
public class Move : Command
|
||||
{
|
||||
public override string CMDName { get { return "move"; } }
|
||||
public override string CMDDesc { get { return "move <get|up|down|east|west|north|south|x y z>: walk or start walking."; } }
|
||||
|
||||
public override string Run(McTcpClient handler, string command)
|
||||
{
|
||||
if (Settings.TerrainAndMovements)
|
||||
{
|
||||
string[] args = getArgs(command);
|
||||
if (args.Length == 1)
|
||||
{
|
||||
string dirStr = getArg(command).Trim().ToLower();
|
||||
Direction direction;
|
||||
switch (dirStr)
|
||||
{
|
||||
case "up": direction = Direction.Up; break;
|
||||
case "down": direction = Direction.Down; break;
|
||||
case "east": direction = Direction.East; break;
|
||||
case "west": direction = Direction.West; break;
|
||||
case "north": direction = Direction.North; break;
|
||||
case "south": direction = Direction.South; break;
|
||||
case "get": return handler.GetCurrentLocation().ToString();
|
||||
default: return "Unknown direction '" + dirStr + "'.";
|
||||
}
|
||||
if (Movement.CanMove(handler.GetWorld(), handler.GetCurrentLocation(), direction))
|
||||
{
|
||||
handler.MoveTo(Movement.Move(handler.GetCurrentLocation(), direction));
|
||||
return "Moving " + dirStr + '.';
|
||||
}
|
||||
else return "Cannot move in that direction.";
|
||||
}
|
||||
else if (args.Length == 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
int x = int.Parse(args[0]);
|
||||
int y = int.Parse(args[1]);
|
||||
int z = int.Parse(args[2]);
|
||||
Location goal = new Location(x, y, z);
|
||||
if (handler.MoveTo(goal))
|
||||
return "Walking to " + goal;
|
||||
return "Failed to compute path to " + goal;
|
||||
}
|
||||
catch (FormatException) { return CMDDesc; }
|
||||
}
|
||||
else return CMDDesc;
|
||||
}
|
||||
else return "Please enable terrainandmovements in config to use this command.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ namespace MinecraftClient.Commands
|
|||
string[] args = getArgs(command);
|
||||
if (args.Length > 0)
|
||||
{
|
||||
if (!Settings.setAccount(args[0]))
|
||||
if (!Settings.SetAccount(args[0]))
|
||||
{
|
||||
return "Unknown account '" + args[0] + "'.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace MinecraftClient.Commands
|
|||
string[] temp = getArg(command).Split('=');
|
||||
if (temp.Length > 1)
|
||||
{
|
||||
if (Settings.setVar(temp[0], getArg(command).Substring(temp[0].Length + 1)))
|
||||
if (Settings.SetVar(temp[0], getArg(command).Substring(temp[0].Length + 1)))
|
||||
{
|
||||
return ""; //Success
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,16 +14,30 @@ namespace MinecraftClient
|
|||
|
||||
public static class ConsoleIO
|
||||
{
|
||||
public static void Reset() { if (reading) { reading = false; Console.Write("\b \b"); } }
|
||||
public static void SetAutoCompleteEngine(IAutoComplete engine) { autocomplete_engine = engine; }
|
||||
public static bool basicIO = false;
|
||||
private static IAutoComplete autocomplete_engine;
|
||||
private static LinkedList<string> previous = new LinkedList<string>();
|
||||
private static readonly object io_lock = new object();
|
||||
private static bool reading = false;
|
||||
private static string buffer = "";
|
||||
private static string buffer2 = "";
|
||||
private static bool reading = false;
|
||||
private static bool reading_lock = false;
|
||||
private static bool writing_lock = false;
|
||||
|
||||
/// <summary>
|
||||
/// Reset the IO mechanism and clear all buffers
|
||||
/// </summary>
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
lock (io_lock)
|
||||
{
|
||||
if (reading)
|
||||
{
|
||||
ClearLineAndBuffer();
|
||||
reading = false;
|
||||
Console.Write("\b \b");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a password from the standard input
|
||||
|
|
@ -31,22 +45,18 @@ namespace MinecraftClient
|
|||
|
||||
public static string ReadPassword()
|
||||
{
|
||||
string password = "";
|
||||
ConsoleKeyInfo k = new ConsoleKeyInfo();
|
||||
while (k.Key != ConsoleKey.Enter)
|
||||
StringBuilder password = new StringBuilder();
|
||||
|
||||
ConsoleKeyInfo k;
|
||||
while ((k = Console.ReadKey(true)).Key != ConsoleKey.Enter)
|
||||
{
|
||||
k = Console.ReadKey(true);
|
||||
switch (k.Key)
|
||||
{
|
||||
case ConsoleKey.Enter:
|
||||
Console.Write('\n');
|
||||
return password;
|
||||
|
||||
case ConsoleKey.Backspace:
|
||||
if (password.Length > 0)
|
||||
{
|
||||
Console.Write("\b \b");
|
||||
password = password.Substring(0, password.Length - 1);
|
||||
password.Remove(password.Length - 1, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -56,7 +66,6 @@ namespace MinecraftClient
|
|||
case ConsoleKey.Home:
|
||||
case ConsoleKey.End:
|
||||
case ConsoleKey.Delete:
|
||||
case ConsoleKey.Oem6:
|
||||
case ConsoleKey.DownArrow:
|
||||
case ConsoleKey.UpArrow:
|
||||
case ConsoleKey.Tab:
|
||||
|
|
@ -66,12 +75,14 @@ namespace MinecraftClient
|
|||
if (k.KeyChar != 0)
|
||||
{
|
||||
Console.Write('*');
|
||||
password += k.KeyChar;
|
||||
password.Append(k.KeyChar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return password;
|
||||
|
||||
Console.WriteLine();
|
||||
return password.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -82,104 +93,106 @@ namespace MinecraftClient
|
|||
{
|
||||
if (basicIO) { return Console.ReadLine(); }
|
||||
ConsoleKeyInfo k = new ConsoleKeyInfo();
|
||||
Console.Write('>');
|
||||
reading = true;
|
||||
buffer = "";
|
||||
buffer2 = "";
|
||||
|
||||
lock (io_lock)
|
||||
{
|
||||
Console.Write('>');
|
||||
reading = true;
|
||||
buffer = "";
|
||||
buffer2 = "";
|
||||
}
|
||||
|
||||
while (k.Key != ConsoleKey.Enter)
|
||||
{
|
||||
k = Console.ReadKey(true);
|
||||
while (writing_lock) { }
|
||||
reading_lock = true;
|
||||
if (k.Key == ConsoleKey.V && k.Modifiers == ConsoleModifiers.Control)
|
||||
lock (io_lock)
|
||||
{
|
||||
string clip = ReadClipboard();
|
||||
foreach (char c in clip)
|
||||
AddChar(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (k.Key)
|
||||
if (k.Key == ConsoleKey.V && k.Modifiers == ConsoleModifiers.Control)
|
||||
{
|
||||
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();
|
||||
string clip = ReadClipboard();
|
||||
foreach (char c in clip)
|
||||
AddChar(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (k.Key)
|
||||
{
|
||||
case ConsoleKey.Escape:
|
||||
ClearLineAndBuffer();
|
||||
break;
|
||||
case ConsoleKey.Backspace:
|
||||
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;
|
||||
case ConsoleKey.Tab:
|
||||
if (autocomplete_engine != null && buffer.Length > 0)
|
||||
{
|
||||
string[] tmp = buffer.Split(' ');
|
||||
if (tmp.Length > 0)
|
||||
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;
|
||||
case ConsoleKey.Tab:
|
||||
if (autocomplete_engine != null && buffer.Length > 0)
|
||||
{
|
||||
string word_tocomplete = tmp[tmp.Length - 1];
|
||||
string word_autocomplete = autocomplete_engine.AutoComplete(buffer);
|
||||
if (!String.IsNullOrEmpty(word_autocomplete) && word_autocomplete != word_tocomplete)
|
||||
if (!String.IsNullOrEmpty(word_autocomplete) && word_autocomplete != buffer)
|
||||
{
|
||||
while (buffer.Length > 0 && buffer[buffer.Length - 1] != ' ') { RemoveOneChar(); }
|
||||
foreach (char c in word_autocomplete) { AddChar(c); }
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (k.KeyChar != 0)
|
||||
AddChar(k.KeyChar);
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
if (k.KeyChar != 0)
|
||||
AddChar(k.KeyChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
reading_lock = false;
|
||||
}
|
||||
while (writing_lock) { }
|
||||
reading = false;
|
||||
previous.AddLast(buffer + buffer2);
|
||||
return buffer + buffer2;
|
||||
|
||||
lock (io_lock)
|
||||
{
|
||||
reading = false;
|
||||
previous.AddLast(buffer + buffer2);
|
||||
return buffer + buffer2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -188,41 +201,47 @@ namespace MinecraftClient
|
|||
|
||||
public static void Write(string text)
|
||||
{
|
||||
if (basicIO) { Console.Write(text); return; }
|
||||
while (reading_lock) { }
|
||||
writing_lock = true;
|
||||
if (reading)
|
||||
if (!basicIO)
|
||||
{
|
||||
ConsoleColor fore = Console.ForegroundColor;
|
||||
ConsoleColor back = Console.BackgroundColor;
|
||||
string buf = buffer;
|
||||
string buf2 = buffer2;
|
||||
ClearLineAndBuffer();
|
||||
if (Console.CursorLeft == 0)
|
||||
lock (io_lock)
|
||||
{
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
Console.CursorTop--;
|
||||
Console.Write(' ');
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
Console.CursorTop--;
|
||||
if (reading)
|
||||
{
|
||||
try
|
||||
{
|
||||
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);
|
||||
buffer = buf;
|
||||
buffer2 = buf2;
|
||||
Console.Write(">" + buffer);
|
||||
if (buffer2.Length > 0)
|
||||
{
|
||||
Console.Write(buffer2 + " \b");
|
||||
for (int i = 0; i < buffer2.Length; i++) { GoBack(); }
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
//Console resized: Try again
|
||||
Console.Write('\n');
|
||||
Write(text);
|
||||
}
|
||||
}
|
||||
else Console.Write(text);
|
||||
}
|
||||
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_lock = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -257,7 +276,7 @@ namespace MinecraftClient
|
|||
if (Settings.chatTimeStamps)
|
||||
{
|
||||
int hour = DateTime.Now.Hour, minute = DateTime.Now.Minute, second = DateTime.Now.Second;
|
||||
ConsoleIO.Write(hour.ToString("00") + ':' + minute.ToString("00") + ':' + second.ToString("00") + ' ');
|
||||
ConsoleIO.Write(String.Format("{0}:{1}:{2} ", hour.ToString("00"), minute.ToString("00"), second.ToString("00")));
|
||||
}
|
||||
if (!acceptnewlines) { str = str.Replace('\n', ' '); }
|
||||
if (ConsoleIO.basicIO) { ConsoleIO.WriteLine(str); return; }
|
||||
|
|
@ -285,7 +304,7 @@ namespace MinecraftClient
|
|||
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;
|
||||
case 'r': Console.ForegroundColor = ConsoleColor.Gray; break;
|
||||
}
|
||||
|
||||
if (subs[i].Length > 1)
|
||||
|
|
@ -294,9 +313,19 @@ namespace MinecraftClient
|
|||
}
|
||||
}
|
||||
}
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
ConsoleIO.Write('\n');
|
||||
}
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Minecraft Console Client Log line
|
||||
/// </summary>
|
||||
/// <param name="text">Text of the log line</param>
|
||||
|
||||
public static void WriteLogLine(string text)
|
||||
{
|
||||
WriteLineFormatted("§8[MCC] " + text);
|
||||
}
|
||||
|
||||
#region Subfunctions
|
||||
|
|
@ -309,15 +338,21 @@ namespace MinecraftClient
|
|||
{
|
||||
if (buffer.Length > 0)
|
||||
{
|
||||
if (Console.CursorLeft == 0)
|
||||
try
|
||||
{
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
Console.CursorTop--;
|
||||
Console.Write(' ');
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
Console.CursorTop--;
|
||||
if (Console.CursorLeft == 0)
|
||||
{
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
if (Console.CursorTop > 0)
|
||||
Console.CursorTop--;
|
||||
Console.Write(' ');
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
if (Console.CursorTop > 0)
|
||||
Console.CursorTop--;
|
||||
}
|
||||
else Console.Write("\b \b");
|
||||
}
|
||||
else Console.Write("\b \b");
|
||||
catch (ArgumentOutOfRangeException) { /* Console was resized!? */ }
|
||||
buffer = buffer.Substring(0, buffer.Length - 1);
|
||||
|
||||
if (buffer2.Length > 0)
|
||||
|
|
@ -329,12 +364,17 @@ namespace MinecraftClient
|
|||
}
|
||||
private static void GoBack()
|
||||
{
|
||||
if (Console.CursorLeft == 0)
|
||||
try
|
||||
{
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
Console.CursorTop--;
|
||||
if (Console.CursorLeft == 0)
|
||||
{
|
||||
Console.CursorLeft = Console.BufferWidth - 1;
|
||||
if (Console.CursorTop > 0)
|
||||
Console.CursorTop--;
|
||||
}
|
||||
else Console.Write('\b');
|
||||
}
|
||||
else Console.Write('\b');
|
||||
catch (ArgumentOutOfRangeException) { /* Console was resized!? */ }
|
||||
}
|
||||
private static void GoLeft()
|
||||
{
|
||||
|
|
@ -383,6 +423,18 @@ namespace MinecraftClient
|
|||
return clipdata;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AutoComplete API
|
||||
/// <summary>
|
||||
/// Set an auto-completion engine for TAB autocompletion
|
||||
/// </summary>
|
||||
/// <param name="engine">Engine implementing the IAutoComplete interface</param>
|
||||
|
||||
public static void SetAutoCompleteEngine(IAutoComplete engine)
|
||||
{
|
||||
autocomplete_engine = engine;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -36,9 +36,13 @@ namespace MinecraftClient
|
|||
{
|
||||
using (HttpWebResponse httpWebReponse = (HttpWebResponse)httpWebRequest.GetResponse())
|
||||
{
|
||||
Bitmap skin = new Bitmap(Image.FromStream(httpWebReponse.GetResponseStream())); //Read skin from network
|
||||
skin = skin.Clone(new Rectangle(8, 8, 8, 8), skin.PixelFormat); //Crop skin
|
||||
SetConsoleIcon(skin.GetHicon()); //Set skin as icon
|
||||
try
|
||||
{
|
||||
Bitmap skin = new Bitmap(Image.FromStream(httpWebReponse.GetResponseStream())); //Read skin from network
|
||||
skin = skin.Clone(new Rectangle(8, 8, 8, 8), skin.PixelFormat); //Crop skin
|
||||
SetConsoleIcon(skin.GetHicon()); //Set skin as icon
|
||||
}
|
||||
catch (ArgumentException) { /* Invalid image in HTTP response */ }
|
||||
}
|
||||
}
|
||||
catch (WebException) //Skin not found? Reset to default icon
|
||||
|
|
|
|||
|
|
@ -197,14 +197,13 @@ namespace MinecraftClient.Crypto
|
|||
/// </summary>
|
||||
/// <param name="underlyingStream">Stream to encrypt</param>
|
||||
/// <param name="AesKey">Key to use</param>
|
||||
/// <param name="paddingProvider">Padding provider for Mono implementation</param>
|
||||
/// <returns>Return an appropriate stream depending on the framework being used</returns>
|
||||
|
||||
public static IAesStream getAesStream(Stream underlyingStream, byte[] AesKey, IPaddingProvider paddingProvider)
|
||||
public static IAesStream getAesStream(Stream underlyingStream, byte[] AesKey)
|
||||
{
|
||||
if (Program.isUsingMono)
|
||||
{
|
||||
return new Streams.MonoAesStream(underlyingStream, AesKey, paddingProvider);
|
||||
return new Streams.MonoAesStream(underlyingStream, AesKey);
|
||||
}
|
||||
else return new Streams.RegularAesStream(underlyingStream, AesKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Crypto
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for padding provider
|
||||
/// Allow to get a padding plugin message from the current network protocol implementation.
|
||||
/// </summary>
|
||||
|
||||
public interface IPaddingProvider
|
||||
{
|
||||
byte[] getPaddingPacket();
|
||||
}
|
||||
}
|
||||
855
MinecraftClient/Crypto/Streams/BouncyAes/AesFastEngine.cs
Normal file
855
MinecraftClient/Crypto/Streams/BouncyAes/AesFastEngine.cs
Normal file
|
|
@ -0,0 +1,855 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Engines
|
||||
{
|
||||
/**
|
||||
* an implementation of the AES (Rijndael)), from FIPS-197.
|
||||
* <p>
|
||||
* For further details see: <a href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/</a>.
|
||||
*
|
||||
* This implementation is based on optimizations from Dr. Brian Gladman's paper and C code at
|
||||
* <a href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/</a>
|
||||
*
|
||||
* There are three levels of tradeoff of speed vs memory
|
||||
* Because java has no preprocessor), they are written as three separate classes from which to choose
|
||||
*
|
||||
* The fastest uses 8Kbytes of static tables to precompute round calculations), 4 256 word tables for encryption
|
||||
* and 4 for decryption.
|
||||
*
|
||||
* The middle performance version uses only one 256 word table for each), for a total of 2Kbytes),
|
||||
* adding 12 rotate operations per round to compute the values contained in the other tables from
|
||||
* the contents of the first
|
||||
*
|
||||
* The slowest version uses no static tables at all and computes the values in each round
|
||||
* </p>
|
||||
* <p>
|
||||
* This file contains the fast version with 8Kbytes of static tables for round precomputation
|
||||
* </p>
|
||||
*/
|
||||
public class AesFastEngine
|
||||
: IBlockCipher
|
||||
{
|
||||
// The S box
|
||||
private static readonly byte[] S =
|
||||
{
|
||||
99, 124, 119, 123, 242, 107, 111, 197,
|
||||
48, 1, 103, 43, 254, 215, 171, 118,
|
||||
202, 130, 201, 125, 250, 89, 71, 240,
|
||||
173, 212, 162, 175, 156, 164, 114, 192,
|
||||
183, 253, 147, 38, 54, 63, 247, 204,
|
||||
52, 165, 229, 241, 113, 216, 49, 21,
|
||||
4, 199, 35, 195, 24, 150, 5, 154,
|
||||
7, 18, 128, 226, 235, 39, 178, 117,
|
||||
9, 131, 44, 26, 27, 110, 90, 160,
|
||||
82, 59, 214, 179, 41, 227, 47, 132,
|
||||
83, 209, 0, 237, 32, 252, 177, 91,
|
||||
106, 203, 190, 57, 74, 76, 88, 207,
|
||||
208, 239, 170, 251, 67, 77, 51, 133,
|
||||
69, 249, 2, 127, 80, 60, 159, 168,
|
||||
81, 163, 64, 143, 146, 157, 56, 245,
|
||||
188, 182, 218, 33, 16, 255, 243, 210,
|
||||
205, 12, 19, 236, 95, 151, 68, 23,
|
||||
196, 167, 126, 61, 100, 93, 25, 115,
|
||||
96, 129, 79, 220, 34, 42, 144, 136,
|
||||
70, 238, 184, 20, 222, 94, 11, 219,
|
||||
224, 50, 58, 10, 73, 6, 36, 92,
|
||||
194, 211, 172, 98, 145, 149, 228, 121,
|
||||
231, 200, 55, 109, 141, 213, 78, 169,
|
||||
108, 86, 244, 234, 101, 122, 174, 8,
|
||||
186, 120, 37, 46, 28, 166, 180, 198,
|
||||
232, 221, 116, 31, 75, 189, 139, 138,
|
||||
112, 62, 181, 102, 72, 3, 246, 14,
|
||||
97, 53, 87, 185, 134, 193, 29, 158,
|
||||
225, 248, 152, 17, 105, 217, 142, 148,
|
||||
155, 30, 135, 233, 206, 85, 40, 223,
|
||||
140, 161, 137, 13, 191, 230, 66, 104,
|
||||
65, 153, 45, 15, 176, 84, 187, 22,
|
||||
};
|
||||
|
||||
// The inverse S-box
|
||||
private static readonly byte[] Si =
|
||||
{
|
||||
82, 9, 106, 213, 48, 54, 165, 56,
|
||||
191, 64, 163, 158, 129, 243, 215, 251,
|
||||
124, 227, 57, 130, 155, 47, 255, 135,
|
||||
52, 142, 67, 68, 196, 222, 233, 203,
|
||||
84, 123, 148, 50, 166, 194, 35, 61,
|
||||
238, 76, 149, 11, 66, 250, 195, 78,
|
||||
8, 46, 161, 102, 40, 217, 36, 178,
|
||||
118, 91, 162, 73, 109, 139, 209, 37,
|
||||
114, 248, 246, 100, 134, 104, 152, 22,
|
||||
212, 164, 92, 204, 93, 101, 182, 146,
|
||||
108, 112, 72, 80, 253, 237, 185, 218,
|
||||
94, 21, 70, 87, 167, 141, 157, 132,
|
||||
144, 216, 171, 0, 140, 188, 211, 10,
|
||||
247, 228, 88, 5, 184, 179, 69, 6,
|
||||
208, 44, 30, 143, 202, 63, 15, 2,
|
||||
193, 175, 189, 3, 1, 19, 138, 107,
|
||||
58, 145, 17, 65, 79, 103, 220, 234,
|
||||
151, 242, 207, 206, 240, 180, 230, 115,
|
||||
150, 172, 116, 34, 231, 173, 53, 133,
|
||||
226, 249, 55, 232, 28, 117, 223, 110,
|
||||
71, 241, 26, 113, 29, 41, 197, 137,
|
||||
111, 183, 98, 14, 170, 24, 190, 27,
|
||||
252, 86, 62, 75, 198, 210, 121, 32,
|
||||
154, 219, 192, 254, 120, 205, 90, 244,
|
||||
31, 221, 168, 51, 136, 7, 199, 49,
|
||||
177, 18, 16, 89, 39, 128, 236, 95,
|
||||
96, 81, 127, 169, 25, 181, 74, 13,
|
||||
45, 229, 122, 159, 147, 201, 156, 239,
|
||||
160, 224, 59, 77, 174, 42, 245, 176,
|
||||
200, 235, 187, 60, 131, 83, 153, 97,
|
||||
23, 43, 4, 126, 186, 119, 214, 38,
|
||||
225, 105, 20, 99, 85, 33, 12, 125,
|
||||
};
|
||||
|
||||
// vector used in calculating key schedule (powers of x in GF(256))
|
||||
private static readonly byte[] rcon =
|
||||
{
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
||||
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91
|
||||
};
|
||||
|
||||
// precomputation tables of calculations for rounds
|
||||
private static readonly uint[] T0 =
|
||||
{
|
||||
0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff,
|
||||
0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102,
|
||||
0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
|
||||
0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa,
|
||||
0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, 0xecadad41,
|
||||
0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
|
||||
0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d,
|
||||
0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
|
||||
0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2,
|
||||
0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795,
|
||||
0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a,
|
||||
0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
|
||||
0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912,
|
||||
0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc,
|
||||
0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7,
|
||||
0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
|
||||
0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040,
|
||||
0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
|
||||
0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0,
|
||||
0x4acfcf85, 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed,
|
||||
0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
|
||||
0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78,
|
||||
0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d, 0xc0404080,
|
||||
0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
|
||||
0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020,
|
||||
0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18,
|
||||
0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488,
|
||||
0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a,
|
||||
0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0,
|
||||
0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
|
||||
0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b,
|
||||
0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
|
||||
0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992,
|
||||
0x0a06060c, 0x6c242448, 0xe45c5cb8, 0x5dc2c29f, 0x6ed3d3bd,
|
||||
0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3,
|
||||
0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
|
||||
0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8,
|
||||
0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4,
|
||||
0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
|
||||
0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
|
||||
0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96,
|
||||
0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
|
||||
0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7,
|
||||
0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969,
|
||||
0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9,
|
||||
0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9,
|
||||
0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715,
|
||||
0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
|
||||
0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65,
|
||||
0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929,
|
||||
0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d,
|
||||
0x3a16162c
|
||||
};
|
||||
|
||||
private static readonly uint[] T1 =
|
||||
{
|
||||
0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d,
|
||||
0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203,
|
||||
0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
|
||||
0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87,
|
||||
0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, 0xadad41ec,
|
||||
0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7,
|
||||
0x7272e496, 0xc0c09b5b, 0xb7b775c2, 0xfdfde11c, 0x93933dae,
|
||||
0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
|
||||
0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293,
|
||||
0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552,
|
||||
0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f,
|
||||
0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
|
||||
0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b,
|
||||
0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2,
|
||||
0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761,
|
||||
0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
|
||||
0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060,
|
||||
0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46,
|
||||
0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8,
|
||||
0xcfcf854a, 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16,
|
||||
0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
|
||||
0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844,
|
||||
0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe, 0x404080c0,
|
||||
0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
|
||||
0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030,
|
||||
0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814,
|
||||
0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc,
|
||||
0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47,
|
||||
0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0,
|
||||
0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
|
||||
0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3,
|
||||
0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
|
||||
0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db,
|
||||
0x06060c0a, 0x2424486c, 0x5c5cb8e4, 0xc2c29f5d, 0xd3d3bd6e,
|
||||
0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337,
|
||||
0x7979f28b, 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7,
|
||||
0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4,
|
||||
0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e,
|
||||
0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
|
||||
0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
|
||||
0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd,
|
||||
0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42,
|
||||
0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701,
|
||||
0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0,
|
||||
0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938,
|
||||
0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970,
|
||||
0x8e8e0789, 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592,
|
||||
0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
|
||||
0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da,
|
||||
0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3, 0x999929b0,
|
||||
0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6,
|
||||
0x16162c3a
|
||||
};
|
||||
|
||||
private static readonly uint[] T2 =
|
||||
{
|
||||
0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2,
|
||||
0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301,
|
||||
0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
|
||||
0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d,
|
||||
0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, 0xad41ecad,
|
||||
0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4,
|
||||
0x72e49672, 0xc09b5bc0, 0xb775c2b7, 0xfde11cfd, 0x933dae93,
|
||||
0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
|
||||
0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371,
|
||||
0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7,
|
||||
0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05,
|
||||
0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
|
||||
0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09,
|
||||
0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e,
|
||||
0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6,
|
||||
0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
|
||||
0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020,
|
||||
0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb,
|
||||
0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858,
|
||||
0xcf854acf, 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb,
|
||||
0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
|
||||
0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c,
|
||||
0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3, 0x4080c040,
|
||||
0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
|
||||
0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010,
|
||||
0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c,
|
||||
0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44,
|
||||
0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d,
|
||||
0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060,
|
||||
0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
|
||||
0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8,
|
||||
0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
|
||||
0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49,
|
||||
0x060c0a06, 0x24486c24, 0x5cb8e45c, 0xc29f5dc2, 0xd3bd6ed3,
|
||||
0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4,
|
||||
0x79f28b79, 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d,
|
||||
0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c,
|
||||
0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a,
|
||||
0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
|
||||
0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
|
||||
0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b,
|
||||
0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e,
|
||||
0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6,
|
||||
0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9,
|
||||
0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, 0xe1d938e1,
|
||||
0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9,
|
||||
0x8e07898e, 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287,
|
||||
0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
|
||||
0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf,
|
||||
0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341, 0x9929b099,
|
||||
0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb,
|
||||
0x162c3a16
|
||||
};
|
||||
|
||||
private static readonly uint[] T3 =
|
||||
{
|
||||
0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2,
|
||||
0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101,
|
||||
0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
|
||||
0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d,
|
||||
0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, 0x41ecadad,
|
||||
0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4,
|
||||
0xe4967272, 0x9b5bc0c0, 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393,
|
||||
0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
|
||||
0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171,
|
||||
0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7,
|
||||
0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505,
|
||||
0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
|
||||
0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909,
|
||||
0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e,
|
||||
0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6,
|
||||
0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
|
||||
0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020,
|
||||
0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb,
|
||||
0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858,
|
||||
0x854acfcf, 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb,
|
||||
0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
|
||||
0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c,
|
||||
0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3, 0x80c04040,
|
||||
0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
|
||||
0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010,
|
||||
0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c,
|
||||
0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444,
|
||||
0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d,
|
||||
0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060,
|
||||
0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
|
||||
0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8,
|
||||
0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
|
||||
0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949,
|
||||
0x0c0a0606, 0x486c2424, 0xb8e45c5c, 0x9f5dc2c2, 0xbd6ed3d3,
|
||||
0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4,
|
||||
0xf28b7979, 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d,
|
||||
0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c,
|
||||
0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a,
|
||||
0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
|
||||
0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
|
||||
0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b,
|
||||
0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e,
|
||||
0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6,
|
||||
0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9,
|
||||
0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, 0xd938e1e1,
|
||||
0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9,
|
||||
0x07898e8e, 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787,
|
||||
0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
|
||||
0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf,
|
||||
0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141, 0x29b09999,
|
||||
0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb,
|
||||
0x2c3a1616
|
||||
};
|
||||
|
||||
private static readonly uint[] Tinv0 =
|
||||
{
|
||||
0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b,
|
||||
0xf1459d1f, 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad,
|
||||
0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
|
||||
0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d,
|
||||
0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, 0xe75f8f03,
|
||||
0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
|
||||
0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899,
|
||||
0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d,
|
||||
0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, 0xe07764b1,
|
||||
0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f,
|
||||
0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3,
|
||||
0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
|
||||
0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a,
|
||||
0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506,
|
||||
0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05,
|
||||
0x75ebf6a4, 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd,
|
||||
0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491,
|
||||
0x055dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
|
||||
0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7,
|
||||
0xdbeec879, 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000,
|
||||
0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
|
||||
0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68,
|
||||
0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793, 0xd296eeb4,
|
||||
0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
|
||||
0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0x0b0d090e,
|
||||
0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af,
|
||||
0xbbdd99ee, 0xfd607fa3, 0x9f2601f7, 0xbcf5725c, 0xc53b6644,
|
||||
0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8,
|
||||
0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85,
|
||||
0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
|
||||
0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411,
|
||||
0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322,
|
||||
0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6,
|
||||
0x28de7aa5, 0x268eb7da, 0xa4bfad3f, 0xe49d3a2c, 0x0d927850,
|
||||
0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e,
|
||||
0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
|
||||
0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd,
|
||||
0xf418596e, 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa,
|
||||
0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
|
||||
0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235,
|
||||
0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, 0x4a9804f1,
|
||||
0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
|
||||
0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1,
|
||||
0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb,
|
||||
0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, 0x8c61d79a,
|
||||
0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7,
|
||||
0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418,
|
||||
0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
|
||||
0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16,
|
||||
0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08,
|
||||
0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48,
|
||||
0x4257b8d0
|
||||
};
|
||||
|
||||
private static readonly uint[] Tinv1 =
|
||||
{
|
||||
0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb,
|
||||
0x459d1ff1, 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6,
|
||||
0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
|
||||
0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1,
|
||||
0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, 0x5f8f03e7,
|
||||
0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3,
|
||||
0x69e04929, 0xc8c98e44, 0x89c2756a, 0x798ef478, 0x3e58996b,
|
||||
0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4,
|
||||
0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, 0x7764b1e0,
|
||||
0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19,
|
||||
0x6cde9487, 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357,
|
||||
0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
|
||||
0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b,
|
||||
0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd, 0xbe0506d5,
|
||||
0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532,
|
||||
0xebf6a475, 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51,
|
||||
0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5,
|
||||
0x5dc47105, 0xd406046f, 0x155060ff, 0xfb981924, 0xe9bdd697,
|
||||
0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738,
|
||||
0xeec879db, 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000,
|
||||
0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
|
||||
0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821,
|
||||
0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f, 0x96eeb4d2,
|
||||
0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16,
|
||||
0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, 0x0d090e0b,
|
||||
0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c,
|
||||
0xdd99eebb, 0x607fa3fd, 0x2601f79f, 0xf5725cbc, 0x3b6644c5,
|
||||
0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863,
|
||||
0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, 0x244a857d,
|
||||
0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
|
||||
0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa,
|
||||
0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef,
|
||||
0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf,
|
||||
0xde7aa528, 0x8eb7da26, 0xbfad3fa4, 0x9d3a2ce4, 0x9278500d,
|
||||
0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e,
|
||||
0xafc382f5, 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3,
|
||||
0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09,
|
||||
0x18596ef4, 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e,
|
||||
0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
|
||||
0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0,
|
||||
0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, 0x9804f14a,
|
||||
0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d,
|
||||
0x4daacc54, 0x0496e4df, 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8,
|
||||
0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e,
|
||||
0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, 0x61d79a8c,
|
||||
0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735,
|
||||
0xe51ce1ed, 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879,
|
||||
0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
|
||||
0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672,
|
||||
0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971, 0xb30c08de,
|
||||
0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874,
|
||||
0x57b8d042
|
||||
};
|
||||
|
||||
private static readonly uint[] Tinv2 =
|
||||
{
|
||||
0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b,
|
||||
0x9d1ff145, 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d,
|
||||
0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
|
||||
0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0,
|
||||
0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, 0x8f03e75f,
|
||||
0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321,
|
||||
0xe0492969, 0xc98e44c8, 0xc2756a89, 0x8ef47879, 0x58996b3e,
|
||||
0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a,
|
||||
0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, 0x64b1e077,
|
||||
0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd,
|
||||
0xde94876c, 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f,
|
||||
0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
|
||||
0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c,
|
||||
0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4, 0x0506d5be,
|
||||
0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1,
|
||||
0xf6a475eb, 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110,
|
||||
0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d,
|
||||
0xc471055d, 0x06046fd4, 0x5060ff15, 0x981924fb, 0xbdd697e9,
|
||||
0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b,
|
||||
0xc879dbee, 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000,
|
||||
0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
|
||||
0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6,
|
||||
0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7, 0xeeb4d296,
|
||||
0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a,
|
||||
0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, 0x090e0b0d,
|
||||
0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07,
|
||||
0x99eebbdd, 0x7fa3fd60, 0x01f79f26, 0x725cbcf5, 0x6644c53b,
|
||||
0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1,
|
||||
0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24,
|
||||
0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
|
||||
0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48,
|
||||
0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90,
|
||||
0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81,
|
||||
0x7aa528de, 0xb7da268e, 0xad3fa4bf, 0x3a2ce49d, 0x78500d92,
|
||||
0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7,
|
||||
0xc382f5af, 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312,
|
||||
0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978,
|
||||
0x596ef418, 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6,
|
||||
0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
|
||||
0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066,
|
||||
0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, 0x04f14a98,
|
||||
0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0,
|
||||
0xaacc544d, 0x96e4df04, 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f,
|
||||
0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41,
|
||||
0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, 0xd79a8c61,
|
||||
0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9,
|
||||
0x1ce1ede5, 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce,
|
||||
0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
|
||||
0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3,
|
||||
0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101, 0x0c08deb3,
|
||||
0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c,
|
||||
0xb8d04257
|
||||
};
|
||||
|
||||
private static readonly uint[] Tinv3 =
|
||||
{
|
||||
0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab,
|
||||
0x1ff1459d, 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76,
|
||||
0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
|
||||
0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe,
|
||||
0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, 0x03e75f8f,
|
||||
0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174,
|
||||
0x492969e0, 0x8e44c8c9, 0x756a89c2, 0xf478798e, 0x996b3e58,
|
||||
0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace,
|
||||
0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, 0xb1e07764,
|
||||
0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45,
|
||||
0x94876cde, 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f,
|
||||
0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
|
||||
0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf,
|
||||
0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da, 0x06d5be05,
|
||||
0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a,
|
||||
0xa475ebf6, 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e,
|
||||
0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54,
|
||||
0x71055dc4, 0x046fd406, 0x60ff1550, 0x1924fb98, 0xd697e9bd,
|
||||
0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19,
|
||||
0x79dbeec8, 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000,
|
||||
0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
|
||||
0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c,
|
||||
0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757, 0xb4d296ee,
|
||||
0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12,
|
||||
0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, 0x0e0b0d09,
|
||||
0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775,
|
||||
0xeebbdd99, 0xa3fd607f, 0xf79f2601, 0x5cbcf572, 0x44c53b66,
|
||||
0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4,
|
||||
0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, 0x857d244a,
|
||||
0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
|
||||
0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894,
|
||||
0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033,
|
||||
0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5,
|
||||
0xa528de7a, 0xda268eb7, 0x3fa4bfad, 0x2ce49d3a, 0x500d9278,
|
||||
0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739,
|
||||
0x82f5afc3, 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225,
|
||||
0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826,
|
||||
0x6ef41859, 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff,
|
||||
0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
|
||||
0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2,
|
||||
0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, 0xf14a9804,
|
||||
0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef,
|
||||
0xcc544daa, 0xe4df0496, 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c,
|
||||
0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b,
|
||||
0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, 0x9a8c61d7,
|
||||
0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961,
|
||||
0xe1ede51c, 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14,
|
||||
0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
|
||||
0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d,
|
||||
0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8, 0x08deb30c,
|
||||
0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c,
|
||||
0xd04257b8
|
||||
};
|
||||
|
||||
private static uint Shift(uint r, int shift)
|
||||
{
|
||||
return (r >> shift) | (r << (32 - shift));
|
||||
}
|
||||
|
||||
/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
|
||||
|
||||
private const uint m1 = 0x80808080;
|
||||
private const uint m2 = 0x7f7f7f7f;
|
||||
private const uint m3 = 0x0000001b;
|
||||
|
||||
private static uint FFmulX(uint x)
|
||||
{
|
||||
return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3);
|
||||
}
|
||||
|
||||
/*
|
||||
The following defines provide alternative definitions of FFmulX that might
|
||||
give improved performance if a fast 32-bit multiply is not available.
|
||||
|
||||
private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x & m2) << 1) ^ ((u >>> 3) | (u >>> 6)); }
|
||||
private static final int m4 = 0x1b1b1b1b;
|
||||
private int FFmulX(int x) { int u = x & m1; return ((x & m2) << 1) ^ ((u - (u >>> 7)) & m4); }
|
||||
|
||||
*/
|
||||
|
||||
private static uint Inv_Mcol(uint x)
|
||||
{
|
||||
uint f2 = FFmulX(x);
|
||||
uint f4 = FFmulX(f2);
|
||||
uint f8 = FFmulX(f4);
|
||||
uint f9 = x ^ f8;
|
||||
|
||||
return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24);
|
||||
}
|
||||
|
||||
private static uint SubWord(uint x)
|
||||
{
|
||||
return (uint)S[x&255]
|
||||
| (((uint)S[(x>>8)&255]) << 8)
|
||||
| (((uint)S[(x>>16)&255]) << 16)
|
||||
| (((uint)S[(x>>24)&255]) << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the necessary round keys
|
||||
* The number of calculations depends on key size and block size
|
||||
* AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
|
||||
* This code is written assuming those are the only possible values
|
||||
*/
|
||||
private uint[][] GenerateWorkingKey(
|
||||
byte[] key,
|
||||
bool forEncryption)
|
||||
{
|
||||
int KC = key.Length / 4; // key length in words
|
||||
|
||||
if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length))
|
||||
throw new ArgumentException("Key length not 128/192/256 bits.");
|
||||
|
||||
ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes
|
||||
|
||||
uint[][] W = new uint[ROUNDS + 1][]; // 4 words in a block
|
||||
for (int i = 0; i <= ROUNDS; ++i)
|
||||
{
|
||||
W[i] = new uint[4];
|
||||
}
|
||||
|
||||
//
|
||||
// copy the key into the round key array
|
||||
//
|
||||
|
||||
int t = 0;
|
||||
for (int i = 0; i < key.Length; t++)
|
||||
{
|
||||
W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i);
|
||||
i+=4;
|
||||
}
|
||||
|
||||
//
|
||||
// while not enough round key material calculated
|
||||
// calculate new values
|
||||
//
|
||||
int k = (ROUNDS + 1) << 2;
|
||||
for (int i = KC; (i < k); i++)
|
||||
{
|
||||
uint temp = W[(i-1)>>2][(i-1)&3];
|
||||
if ((i % KC) == 0) {
|
||||
temp = SubWord(Shift(temp, 8)) ^ rcon[(i / KC)-1];
|
||||
} else if ((KC > 6) && ((i % KC) == 4)) {
|
||||
temp = SubWord(temp);
|
||||
}
|
||||
|
||||
W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp;
|
||||
}
|
||||
|
||||
if (!forEncryption)
|
||||
{
|
||||
for (int j = 1; j < ROUNDS; j++)
|
||||
{
|
||||
uint[] w = W[j];
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
w[i] = Inv_Mcol(w[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return W;
|
||||
}
|
||||
|
||||
private int ROUNDS;
|
||||
private uint[][] WorkingKey;
|
||||
private uint C0, C1, C2, C3;
|
||||
private bool forEncryption;
|
||||
|
||||
private const int BLOCK_SIZE = 16;
|
||||
|
||||
/**
|
||||
* default constructor - 128 bit block size.
|
||||
*/
|
||||
public AesFastEngine()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* initialise an AES cipher.
|
||||
*
|
||||
* @param forEncryption whether or not we are for encryption.
|
||||
* @param parameters the parameters required to set up the cipher.
|
||||
* @exception ArgumentException if the parameters argument is
|
||||
* inappropriate.
|
||||
*/
|
||||
public virtual void Init(
|
||||
bool forEncryption,
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
KeyParameter keyParameter = parameters as KeyParameter;
|
||||
|
||||
if (keyParameter == null)
|
||||
throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().Name);
|
||||
|
||||
WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption);
|
||||
|
||||
this.forEncryption = forEncryption;
|
||||
}
|
||||
|
||||
public virtual string AlgorithmName
|
||||
{
|
||||
get { return "AES"; }
|
||||
}
|
||||
|
||||
public virtual bool IsPartialBlockOkay
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual int GetBlockSize()
|
||||
{
|
||||
return BLOCK_SIZE;
|
||||
}
|
||||
|
||||
public virtual int ProcessBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
if (WorkingKey == null)
|
||||
throw new InvalidOperationException("AES engine not initialised");
|
||||
|
||||
Check.DataLength(input, inOff, 16, "input buffer too short");
|
||||
Check.OutputLength(output, outOff, 16, "output buffer too short");
|
||||
|
||||
UnPackBlock(input, inOff);
|
||||
|
||||
if (forEncryption)
|
||||
{
|
||||
EncryptBlock(WorkingKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecryptBlock(WorkingKey);
|
||||
}
|
||||
|
||||
PackBlock(output, outOff);
|
||||
|
||||
return BLOCK_SIZE;
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
private void UnPackBlock(
|
||||
byte[] bytes,
|
||||
int off)
|
||||
{
|
||||
C0 = Pack.LE_To_UInt32(bytes, off);
|
||||
C1 = Pack.LE_To_UInt32(bytes, off + 4);
|
||||
C2 = Pack.LE_To_UInt32(bytes, off + 8);
|
||||
C3 = Pack.LE_To_UInt32(bytes, off + 12);
|
||||
}
|
||||
|
||||
private void PackBlock(
|
||||
byte[] bytes,
|
||||
int off)
|
||||
{
|
||||
Pack.UInt32_To_LE(C0, bytes, off);
|
||||
Pack.UInt32_To_LE(C1, bytes, off + 4);
|
||||
Pack.UInt32_To_LE(C2, bytes, off + 8);
|
||||
Pack.UInt32_To_LE(C3, bytes, off + 12);
|
||||
}
|
||||
|
||||
private void EncryptBlock(uint[][] KW)
|
||||
{
|
||||
uint[] kw = KW[0];
|
||||
uint t0 = this.C0 ^ kw[0];
|
||||
uint t1 = this.C1 ^ kw[1];
|
||||
uint t2 = this.C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = this.C3 ^ kw[3];
|
||||
int r = 1;
|
||||
while (r < ROUNDS - 1)
|
||||
{
|
||||
kw = KW[r++];
|
||||
r0 = T0[t0 & 255] ^ T1[(t1 >> 8) & 255] ^ T2[(t2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0];
|
||||
r1 = T0[t1 & 255] ^ T1[(t2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[t0 >> 24] ^ kw[1];
|
||||
r2 = T0[t2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(t0 >> 16) & 255] ^ T3[t1 >> 24] ^ kw[2];
|
||||
r3 = T0[r3 & 255] ^ T1[(t0 >> 8) & 255] ^ T2[(t1 >> 16) & 255] ^ T3[t2 >> 24] ^ kw[3];
|
||||
kw = KW[r++];
|
||||
t0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0];
|
||||
t1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ kw[1];
|
||||
t2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ kw[2];
|
||||
r3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ kw[3];
|
||||
}
|
||||
|
||||
kw = KW[r++];
|
||||
r0 = T0[t0 & 255] ^ T1[(t1 >> 8) & 255] ^ T2[(t2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0];
|
||||
r1 = T0[t1 & 255] ^ T1[(t2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[t0 >> 24] ^ kw[1];
|
||||
r2 = T0[t2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(t0 >> 16) & 255] ^ T3[t1 >> 24] ^ kw[2];
|
||||
r3 = T0[r3 & 255] ^ T1[(t0 >> 8) & 255] ^ T2[(t1 >> 16) & 255] ^ T3[t2 >> 24] ^ kw[3];
|
||||
|
||||
// the final round's table is a simple function of S so we don't use a whole other four tables for it
|
||||
|
||||
kw = KW[r];
|
||||
this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ kw[0];
|
||||
this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ kw[1];
|
||||
this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ kw[2];
|
||||
this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ kw[3];
|
||||
}
|
||||
|
||||
private void DecryptBlock(uint[][] KW)
|
||||
{
|
||||
uint[] kw = KW[ROUNDS];
|
||||
uint t0 = this.C0 ^ kw[0];
|
||||
uint t1 = this.C1 ^ kw[1];
|
||||
uint t2 = this.C2 ^ kw[2];
|
||||
|
||||
uint r0, r1, r2, r3 = this.C3 ^ kw[3];
|
||||
int r = ROUNDS - 1;
|
||||
while (r > 1)
|
||||
{
|
||||
kw = KW[r--];
|
||||
r0 = Tinv0[t0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(t2 >> 16) & 255] ^ Tinv3[t1 >> 24] ^ kw[0];
|
||||
r1 = Tinv0[t1 & 255] ^ Tinv1[(t0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[t2 >> 24] ^ kw[1];
|
||||
r2 = Tinv0[t2 & 255] ^ Tinv1[(t1 >> 8) & 255] ^ Tinv2[(t0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Tinv1[(t2 >> 8) & 255] ^ Tinv2[(t1 >> 16) & 255] ^ Tinv3[t0 >> 24] ^ kw[3];
|
||||
kw = KW[r--];
|
||||
t0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ kw[0];
|
||||
t1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ kw[1];
|
||||
t2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ kw[3];
|
||||
}
|
||||
|
||||
kw = KW[1];
|
||||
r0 = Tinv0[t0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(t2 >> 16) & 255] ^ Tinv3[t1 >> 24] ^ kw[0];
|
||||
r1 = Tinv0[t1 & 255] ^ Tinv1[(t0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[t2 >> 24] ^ kw[1];
|
||||
r2 = Tinv0[t2 & 255] ^ Tinv1[(t1 >> 8) & 255] ^ Tinv2[(t0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2];
|
||||
r3 = Tinv0[r3 & 255] ^ Tinv1[(t2 >> 8) & 255] ^ Tinv2[(t1 >> 16) & 255] ^ Tinv3[t0 >> 24] ^ kw[3];
|
||||
|
||||
// the final round's table is a simple function of Si so we don't use a whole other four tables for it
|
||||
|
||||
kw = KW[0];
|
||||
this.C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ kw[0];
|
||||
this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ kw[1];
|
||||
this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ kw[2];
|
||||
this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ kw[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
367
MinecraftClient/Crypto/Streams/BouncyAes/BufferedBlockCipher.cs
Normal file
367
MinecraftClient/Crypto/Streams/BouncyAes/BufferedBlockCipher.cs
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
/**
|
||||
* A wrapper class that allows block ciphers to be used to process data in
|
||||
* a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
|
||||
* buffer is full and more data is being added, or on a doFinal.
|
||||
* <p>
|
||||
* Note: in the case where the underlying cipher is either a CFB cipher or an
|
||||
* OFB one the last block may not be a multiple of the block size.
|
||||
* </p>
|
||||
*/
|
||||
public class BufferedBlockCipher
|
||||
: BufferedCipherBase
|
||||
{
|
||||
internal byte[] buf;
|
||||
internal int bufOff;
|
||||
internal bool forEncryption;
|
||||
internal IBlockCipher cipher;
|
||||
|
||||
/**
|
||||
* constructor for subclasses
|
||||
*/
|
||||
protected BufferedBlockCipher()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a buffered block cipher without padding.
|
||||
*
|
||||
* @param cipher the underlying block cipher this buffering object wraps.
|
||||
* false otherwise.
|
||||
*/
|
||||
public BufferedBlockCipher(
|
||||
IBlockCipher cipher)
|
||||
{
|
||||
if (cipher == null)
|
||||
throw new ArgumentNullException("cipher");
|
||||
|
||||
this.cipher = cipher;
|
||||
buf = new byte[cipher.GetBlockSize()];
|
||||
bufOff = 0;
|
||||
}
|
||||
|
||||
public override string AlgorithmName
|
||||
{
|
||||
get { return cipher.AlgorithmName; }
|
||||
}
|
||||
|
||||
/**
|
||||
* initialise the cipher.
|
||||
*
|
||||
* @param forEncryption if true the cipher is initialised for
|
||||
* encryption, if false for decryption.
|
||||
* @param param the key and other data required by the cipher.
|
||||
* @exception ArgumentException if the parameters argument is
|
||||
* inappropriate.
|
||||
*/
|
||||
// Note: This doubles as the Init in the event that this cipher is being used as an IWrapper
|
||||
public override void Init(
|
||||
bool forEncryption,
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
this.forEncryption = forEncryption;
|
||||
|
||||
//ParametersWithRandom pwr = parameters as ParametersWithRandom;
|
||||
//if (pwr != null)
|
||||
// parameters = pwr.Parameters;
|
||||
|
||||
Reset();
|
||||
|
||||
cipher.Init(forEncryption, parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the blocksize for the underlying cipher.
|
||||
*
|
||||
* @return the blocksize for the underlying cipher.
|
||||
*/
|
||||
public override int GetBlockSize()
|
||||
{
|
||||
return cipher.GetBlockSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* return the size of the output buffer required for an update
|
||||
* an input of len bytes.
|
||||
*
|
||||
* @param len the length of the input.
|
||||
* @return the space required to accommodate a call to update
|
||||
* with len bytes of input.
|
||||
*/
|
||||
public override int GetUpdateOutputSize(
|
||||
int length)
|
||||
{
|
||||
int total = length + bufOff;
|
||||
int leftOver = total % buf.Length;
|
||||
return total - leftOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the size of the output buffer required for an update plus a
|
||||
* doFinal with an input of len bytes.
|
||||
*
|
||||
* @param len the length of the input.
|
||||
* @return the space required to accommodate a call to update and doFinal
|
||||
* with len bytes of input.
|
||||
*/
|
||||
public override int GetOutputSize(
|
||||
int length)
|
||||
{
|
||||
// Note: Can assume IsPartialBlockOkay is true for purposes of this calculation
|
||||
return length + bufOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* process a single byte, producing an output block if necessary.
|
||||
*
|
||||
* @param in the input byte.
|
||||
* @param out the space for any output that might be produced.
|
||||
* @param outOff the offset from which the output will be copied.
|
||||
* @return the number of output bytes copied to out.
|
||||
* @exception DataLengthException if there isn't enough space in out.
|
||||
* @exception InvalidOperationException if the cipher isn't initialised.
|
||||
*/
|
||||
public override int ProcessByte(
|
||||
byte input,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
buf[bufOff++] = input;
|
||||
|
||||
if (bufOff == buf.Length)
|
||||
{
|
||||
if ((outOff + buf.Length) > output.Length)
|
||||
throw new DataLengthException("output buffer too short");
|
||||
|
||||
bufOff = 0;
|
||||
return cipher.ProcessBlock(buf, 0, output, outOff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override byte[] ProcessByte(
|
||||
byte input)
|
||||
{
|
||||
int outLength = GetUpdateOutputSize(1);
|
||||
|
||||
byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
|
||||
|
||||
int pos = ProcessByte(input, outBytes, 0);
|
||||
|
||||
if (outLength > 0 && pos < outLength)
|
||||
{
|
||||
byte[] tmp = new byte[pos];
|
||||
Array.Copy(outBytes, 0, tmp, 0, pos);
|
||||
outBytes = tmp;
|
||||
}
|
||||
|
||||
return outBytes;
|
||||
}
|
||||
|
||||
public override byte[] ProcessBytes(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
if (length < 1)
|
||||
return null;
|
||||
|
||||
int outLength = GetUpdateOutputSize(length);
|
||||
|
||||
byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
|
||||
|
||||
int pos = ProcessBytes(input, inOff, length, outBytes, 0);
|
||||
|
||||
if (outLength > 0 && pos < outLength)
|
||||
{
|
||||
byte[] tmp = new byte[pos];
|
||||
Array.Copy(outBytes, 0, tmp, 0, pos);
|
||||
outBytes = tmp;
|
||||
}
|
||||
|
||||
return outBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* process an array of bytes, producing output if necessary.
|
||||
*
|
||||
* @param in the input byte array.
|
||||
* @param inOff the offset at which the input data starts.
|
||||
* @param len the number of bytes to be copied out of the input array.
|
||||
* @param out the space for any output that might be produced.
|
||||
* @param outOff the offset from which the output will be copied.
|
||||
* @return the number of output bytes copied to out.
|
||||
* @exception DataLengthException if there isn't enough space in out.
|
||||
* @exception InvalidOperationException if the cipher isn't initialised.
|
||||
*/
|
||||
public override int ProcessBytes(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
if (length < 1)
|
||||
{
|
||||
if (length < 0)
|
||||
throw new ArgumentException("Can't have a negative input length!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int blockSize = GetBlockSize();
|
||||
int outLength = GetUpdateOutputSize(length);
|
||||
|
||||
if (outLength > 0)
|
||||
{
|
||||
Check.OutputLength(output, outOff, outLength, "output buffer too short");
|
||||
}
|
||||
|
||||
int resultLen = 0;
|
||||
int gapLen = buf.Length - bufOff;
|
||||
if (length > gapLen)
|
||||
{
|
||||
Array.Copy(input, inOff, buf, bufOff, gapLen);
|
||||
resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
|
||||
bufOff = 0;
|
||||
length -= gapLen;
|
||||
inOff += gapLen;
|
||||
while (length > buf.Length)
|
||||
{
|
||||
resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen);
|
||||
length -= blockSize;
|
||||
inOff += blockSize;
|
||||
}
|
||||
}
|
||||
Array.Copy(input, inOff, buf, bufOff, length);
|
||||
bufOff += length;
|
||||
if (bufOff == buf.Length)
|
||||
{
|
||||
resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
|
||||
bufOff = 0;
|
||||
}
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
public override byte[] DoFinal()
|
||||
{
|
||||
byte[] outBytes = EmptyBuffer;
|
||||
|
||||
int length = GetOutputSize(0);
|
||||
if (length > 0)
|
||||
{
|
||||
outBytes = new byte[length];
|
||||
|
||||
int pos = DoFinal(outBytes, 0);
|
||||
if (pos < outBytes.Length)
|
||||
{
|
||||
byte[] tmp = new byte[pos];
|
||||
Array.Copy(outBytes, 0, tmp, 0, pos);
|
||||
outBytes = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
return outBytes;
|
||||
}
|
||||
|
||||
public override byte[] DoFinal(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int inLen)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
int length = GetOutputSize(inLen);
|
||||
|
||||
byte[] outBytes = EmptyBuffer;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
outBytes = new byte[length];
|
||||
|
||||
int pos = (inLen > 0)
|
||||
? ProcessBytes(input, inOff, inLen, outBytes, 0)
|
||||
: 0;
|
||||
|
||||
pos += DoFinal(outBytes, pos);
|
||||
|
||||
if (pos < outBytes.Length)
|
||||
{
|
||||
byte[] tmp = new byte[pos];
|
||||
Array.Copy(outBytes, 0, tmp, 0, pos);
|
||||
outBytes = tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
return outBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the last block in the buffer.
|
||||
*
|
||||
* @param out the array the block currently being held is copied into.
|
||||
* @param outOff the offset at which the copying starts.
|
||||
* @return the number of output bytes copied to out.
|
||||
* @exception DataLengthException if there is insufficient space in out for
|
||||
* the output, or the input is not block size aligned and should be.
|
||||
* @exception InvalidOperationException if the underlying cipher is not
|
||||
* initialised.
|
||||
* @exception InvalidCipherTextException if padding is expected and not found.
|
||||
* @exception DataLengthException if the input is not block size
|
||||
* aligned.
|
||||
*/
|
||||
public override int DoFinal(
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (bufOff != 0)
|
||||
{
|
||||
Check.DataLength(!cipher.IsPartialBlockOkay, "data not block size aligned");
|
||||
Check.OutputLength(output, outOff, bufOff, "output buffer too short for DoFinal()");
|
||||
|
||||
// NB: Can't copy directly, or we may write too much output
|
||||
cipher.ProcessBlock(buf, 0, buf, 0);
|
||||
Array.Copy(buf, 0, output, outOff, bufOff);
|
||||
}
|
||||
|
||||
return bufOff;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the buffer and cipher. After resetting the object is in the same
|
||||
* state as it was after the last init (if there was one).
|
||||
*/
|
||||
public override void Reset()
|
||||
{
|
||||
Array.Clear(buf, 0, buf.Length);
|
||||
bufOff = 0;
|
||||
|
||||
cipher.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
113
MinecraftClient/Crypto/Streams/BouncyAes/BufferedCipherBase.cs
Normal file
113
MinecraftClient/Crypto/Streams/BouncyAes/BufferedCipherBase.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
public abstract class BufferedCipherBase
|
||||
: IBufferedCipher
|
||||
{
|
||||
protected static readonly byte[] EmptyBuffer = new byte[0];
|
||||
|
||||
public abstract string AlgorithmName { get; }
|
||||
|
||||
public abstract void Init(bool forEncryption, ICipherParameters parameters);
|
||||
|
||||
public abstract int GetBlockSize();
|
||||
|
||||
public abstract int GetOutputSize(int inputLen);
|
||||
public abstract int GetUpdateOutputSize(int inputLen);
|
||||
|
||||
public abstract byte[] ProcessByte(byte input);
|
||||
|
||||
public virtual int ProcessByte(
|
||||
byte input,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
byte[] outBytes = ProcessByte(input);
|
||||
if (outBytes == null)
|
||||
return 0;
|
||||
if (outOff + outBytes.Length > output.Length)
|
||||
throw new DataLengthException("output buffer too short");
|
||||
outBytes.CopyTo(output, outOff);
|
||||
return outBytes.Length;
|
||||
}
|
||||
|
||||
public virtual byte[] ProcessBytes(
|
||||
byte[] input)
|
||||
{
|
||||
return ProcessBytes(input, 0, input.Length);
|
||||
}
|
||||
|
||||
public abstract byte[] ProcessBytes(byte[] input, int inOff, int length);
|
||||
|
||||
public virtual int ProcessBytes(
|
||||
byte[] input,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
return ProcessBytes(input, 0, input.Length, output, outOff);
|
||||
}
|
||||
|
||||
public virtual int ProcessBytes(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
byte[] outBytes = ProcessBytes(input, inOff, length);
|
||||
if (outBytes == null)
|
||||
return 0;
|
||||
if (outOff + outBytes.Length > output.Length)
|
||||
throw new DataLengthException("output buffer too short");
|
||||
outBytes.CopyTo(output, outOff);
|
||||
return outBytes.Length;
|
||||
}
|
||||
|
||||
public abstract byte[] DoFinal();
|
||||
|
||||
public virtual byte[] DoFinal(
|
||||
byte[] input)
|
||||
{
|
||||
return DoFinal(input, 0, input.Length);
|
||||
}
|
||||
|
||||
public abstract byte[] DoFinal(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length);
|
||||
|
||||
public virtual int DoFinal(
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
byte[] outBytes = DoFinal();
|
||||
if (outOff + outBytes.Length > output.Length)
|
||||
throw new DataLengthException("output buffer too short");
|
||||
outBytes.CopyTo(output, outOff);
|
||||
return outBytes.Length;
|
||||
}
|
||||
|
||||
public virtual int DoFinal(
|
||||
byte[] input,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
return DoFinal(input, 0, input.Length, output, outOff);
|
||||
}
|
||||
|
||||
public virtual int DoFinal(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
int length,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
int len = ProcessBytes(input, inOff, length, output, outOff);
|
||||
len += DoFinal(output, outOff + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
public abstract void Reset();
|
||||
}
|
||||
}
|
||||
224
MinecraftClient/Crypto/Streams/BouncyAes/CfbBlockCipher.cs
Normal file
224
MinecraftClient/Crypto/Streams/BouncyAes/CfbBlockCipher.cs
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Modes
|
||||
{
|
||||
/**
|
||||
* implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
|
||||
*/
|
||||
public class CfbBlockCipher
|
||||
: IBlockCipher
|
||||
{
|
||||
private byte[] IV;
|
||||
private byte[] cfbV;
|
||||
private byte[] cfbOutV;
|
||||
private bool encrypting;
|
||||
|
||||
private readonly int blockSize;
|
||||
private readonly IBlockCipher cipher;
|
||||
|
||||
/**
|
||||
* Basic constructor.
|
||||
*
|
||||
* @param cipher the block cipher to be used as the basis of the
|
||||
* feedback mode.
|
||||
* @param blockSize the block size in bits (note: a multiple of 8)
|
||||
*/
|
||||
public CfbBlockCipher(
|
||||
IBlockCipher cipher,
|
||||
int bitBlockSize)
|
||||
{
|
||||
this.cipher = cipher;
|
||||
this.blockSize = bitBlockSize / 8;
|
||||
this.IV = new byte[cipher.GetBlockSize()];
|
||||
this.cfbV = new byte[cipher.GetBlockSize()];
|
||||
this.cfbOutV = new byte[cipher.GetBlockSize()];
|
||||
}
|
||||
/**
|
||||
* return the underlying block cipher that we are wrapping.
|
||||
*
|
||||
* @return the underlying block cipher that we are wrapping.
|
||||
*/
|
||||
public IBlockCipher GetUnderlyingCipher()
|
||||
{
|
||||
return cipher;
|
||||
}
|
||||
/**
|
||||
* Initialise the cipher and, possibly, the initialisation vector (IV).
|
||||
* If an IV isn't passed as part of the parameter, the IV will be all zeros.
|
||||
* An IV which is too short is handled in FIPS compliant fashion.
|
||||
*
|
||||
* @param forEncryption if true the cipher is initialised for
|
||||
* encryption, if false for decryption.
|
||||
* @param param the key and other data required by the cipher.
|
||||
* @exception ArgumentException if the parameters argument is
|
||||
* inappropriate.
|
||||
*/
|
||||
public void Init(
|
||||
bool forEncryption,
|
||||
ICipherParameters parameters)
|
||||
{
|
||||
this.encrypting = forEncryption;
|
||||
if (parameters is ParametersWithIV)
|
||||
{
|
||||
ParametersWithIV ivParam = (ParametersWithIV) parameters;
|
||||
byte[] iv = ivParam.GetIV();
|
||||
int diff = IV.Length - iv.Length;
|
||||
Array.Copy(iv, 0, IV, diff, iv.Length);
|
||||
Array.Clear(IV, 0, diff);
|
||||
|
||||
parameters = ivParam.Parameters;
|
||||
}
|
||||
Reset();
|
||||
|
||||
// if it's null, key is to be reused.
|
||||
if (parameters != null)
|
||||
{
|
||||
cipher.Init(true, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the algorithm name and mode.
|
||||
*
|
||||
* @return the name of the underlying algorithm followed by "/CFB"
|
||||
* and the block size in bits.
|
||||
*/
|
||||
public string AlgorithmName
|
||||
{
|
||||
get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); }
|
||||
}
|
||||
|
||||
public bool IsPartialBlockOkay
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/**
|
||||
* return the block size we are operating at.
|
||||
*
|
||||
* @return the block size we are operating at (in bytes).
|
||||
*/
|
||||
public int GetBlockSize()
|
||||
{
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process one block of input from the array in and write it to
|
||||
* the out array.
|
||||
*
|
||||
* @param in the array containing the input data.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the output data will be copied into.
|
||||
* @param outOff the offset into the out array the output will start at.
|
||||
* @exception DataLengthException if there isn't enough data in in, or
|
||||
* space in out.
|
||||
* @exception InvalidOperationException if the cipher isn't initialised.
|
||||
* @return the number of bytes processed and produced.
|
||||
*/
|
||||
public int ProcessBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] output,
|
||||
int outOff)
|
||||
{
|
||||
return (encrypting)
|
||||
? EncryptBlock(input, inOff, output, outOff)
|
||||
: DecryptBlock(input, inOff, output, outOff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the appropriate processing for CFB mode encryption.
|
||||
*
|
||||
* @param in the array containing the data to be encrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted data will be copied into.
|
||||
* @param outOff the offset into the out array the output will start at.
|
||||
* @exception DataLengthException if there isn't enough data in in, or
|
||||
* space in out.
|
||||
* @exception InvalidOperationException if the cipher isn't initialised.
|
||||
* @return the number of bytes processed and produced.
|
||||
*/
|
||||
public int EncryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
if ((outOff + blockSize) > outBytes.Length)
|
||||
{
|
||||
throw new DataLengthException("output buffer too short");
|
||||
}
|
||||
cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
|
||||
//
|
||||
// XOR the cfbV with the plaintext producing the ciphertext
|
||||
//
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
|
||||
}
|
||||
//
|
||||
// change over the input block.
|
||||
//
|
||||
Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
|
||||
Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize);
|
||||
return blockSize;
|
||||
}
|
||||
/**
|
||||
* Do the appropriate processing for CFB mode decryption.
|
||||
*
|
||||
* @param in the array containing the data to be decrypted.
|
||||
* @param inOff offset into the in array the data starts at.
|
||||
* @param out the array the encrypted data will be copied into.
|
||||
* @param outOff the offset into the out array the output will start at.
|
||||
* @exception DataLengthException if there isn't enough data in in, or
|
||||
* space in out.
|
||||
* @exception InvalidOperationException if the cipher isn't initialised.
|
||||
* @return the number of bytes processed and produced.
|
||||
*/
|
||||
public int DecryptBlock(
|
||||
byte[] input,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
if ((inOff + blockSize) > input.Length)
|
||||
{
|
||||
throw new DataLengthException("input buffer too short");
|
||||
}
|
||||
if ((outOff + blockSize) > outBytes.Length)
|
||||
{
|
||||
throw new DataLengthException("output buffer too short");
|
||||
}
|
||||
cipher.ProcessBlock(cfbV, 0, cfbOutV, 0);
|
||||
//
|
||||
// change over the input block.
|
||||
//
|
||||
Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize);
|
||||
Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize);
|
||||
//
|
||||
// XOR the cfbV with the ciphertext producing the plaintext
|
||||
//
|
||||
for (int i = 0; i < blockSize; i++)
|
||||
{
|
||||
outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]);
|
||||
}
|
||||
return blockSize;
|
||||
}
|
||||
/**
|
||||
* reset the chaining vector back to the IV and reset the underlying
|
||||
* cipher.
|
||||
*/
|
||||
public void Reset()
|
||||
{
|
||||
Array.Copy(IV, 0, cfbV, 0, IV.Length);
|
||||
cipher.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
MinecraftClient/Crypto/Streams/BouncyAes/Check.cs
Normal file
25
MinecraftClient/Crypto/Streams/BouncyAes/Check.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
internal class Check
|
||||
{
|
||||
internal static void DataLength(bool condition, string msg)
|
||||
{
|
||||
if (condition)
|
||||
throw new DataLengthException(msg);
|
||||
}
|
||||
|
||||
internal static void DataLength(byte[] buf, int off, int len, string msg)
|
||||
{
|
||||
if (off + len > buf.Length)
|
||||
throw new DataLengthException(msg);
|
||||
}
|
||||
|
||||
internal static void OutputLength(byte[] buf, int off, int len, string msg)
|
||||
{
|
||||
if (off + len > buf.Length)
|
||||
throw new OutputLengthException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
234
MinecraftClient/Crypto/Streams/BouncyAes/CipherStream.cs
Normal file
234
MinecraftClient/Crypto/Streams/BouncyAes/CipherStream.cs
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.IO
|
||||
{
|
||||
public class CipherStream
|
||||
: Stream
|
||||
{
|
||||
internal Stream stream;
|
||||
internal IBufferedCipher inCipher, outCipher;
|
||||
private byte[] mInBuf;
|
||||
private int mInPos;
|
||||
private bool inStreamEnded;
|
||||
|
||||
public CipherStream(
|
||||
Stream stream,
|
||||
IBufferedCipher readCipher,
|
||||
IBufferedCipher writeCipher)
|
||||
{
|
||||
this.stream = stream;
|
||||
|
||||
if (readCipher != null)
|
||||
{
|
||||
this.inCipher = readCipher;
|
||||
mInBuf = null;
|
||||
}
|
||||
|
||||
if (writeCipher != null)
|
||||
{
|
||||
this.outCipher = writeCipher;
|
||||
}
|
||||
}
|
||||
|
||||
public IBufferedCipher ReadCipher
|
||||
{
|
||||
get { return inCipher; }
|
||||
}
|
||||
|
||||
public IBufferedCipher WriteCipher
|
||||
{
|
||||
get { return outCipher; }
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
if (inCipher == null)
|
||||
return stream.ReadByte();
|
||||
|
||||
if (mInBuf == null || mInPos >= mInBuf.Length)
|
||||
{
|
||||
if (!FillInBuf())
|
||||
return -1;
|
||||
}
|
||||
|
||||
return mInBuf[mInPos++];
|
||||
}
|
||||
|
||||
public override int Read(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count)
|
||||
{
|
||||
if (inCipher == null)
|
||||
return stream.Read(buffer, offset, count);
|
||||
|
||||
int num = 0;
|
||||
while (num < count)
|
||||
{
|
||||
if (mInBuf == null || mInPos >= mInBuf.Length)
|
||||
{
|
||||
if (!FillInBuf())
|
||||
break;
|
||||
}
|
||||
|
||||
int numToCopy = System.Math.Min(count - num, mInBuf.Length - mInPos);
|
||||
Array.Copy(mInBuf, mInPos, buffer, offset + num, numToCopy);
|
||||
mInPos += numToCopy;
|
||||
num += numToCopy;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
private bool FillInBuf()
|
||||
{
|
||||
if (inStreamEnded)
|
||||
return false;
|
||||
|
||||
mInPos = 0;
|
||||
|
||||
do
|
||||
{
|
||||
mInBuf = ReadAndProcessBlock();
|
||||
}
|
||||
while (!inStreamEnded && mInBuf == null);
|
||||
|
||||
return mInBuf != null;
|
||||
}
|
||||
|
||||
private byte[] ReadAndProcessBlock()
|
||||
{
|
||||
int blockSize = inCipher.GetBlockSize();
|
||||
int readSize = (blockSize == 0) ? 256 : blockSize;
|
||||
|
||||
byte[] block = new byte[readSize];
|
||||
int numRead = 0;
|
||||
do
|
||||
{
|
||||
int count = stream.Read(block, numRead, block.Length - numRead);
|
||||
if (count < 1)
|
||||
{
|
||||
inStreamEnded = true;
|
||||
break;
|
||||
}
|
||||
numRead += count;
|
||||
}
|
||||
while (numRead < block.Length);
|
||||
|
||||
Debug.Assert(inStreamEnded || numRead == block.Length);
|
||||
|
||||
byte[] bytes = inStreamEnded
|
||||
? inCipher.DoFinal(block, 0, numRead)
|
||||
: inCipher.ProcessBytes(block);
|
||||
|
||||
if (bytes != null && bytes.Length == 0)
|
||||
{
|
||||
bytes = null;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public override void Write(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count)
|
||||
{
|
||||
Debug.Assert(buffer != null);
|
||||
Debug.Assert(0 <= offset && offset <= buffer.Length);
|
||||
Debug.Assert(count >= 0);
|
||||
|
||||
int end = offset + count;
|
||||
|
||||
Debug.Assert(0 <= end && end <= buffer.Length);
|
||||
|
||||
if (outCipher == null)
|
||||
{
|
||||
stream.Write(buffer, offset, count);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = outCipher.ProcessBytes(buffer, offset, count);
|
||||
if (data != null)
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteByte(
|
||||
byte b)
|
||||
{
|
||||
if (outCipher == null)
|
||||
{
|
||||
stream.WriteByte(b);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = outCipher.ProcessByte(b);
|
||||
if (data != null)
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return stream.CanRead && (inCipher != null); }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return stream.CanWrite && (outCipher != null); }
|
||||
}
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public sealed override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public sealed override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (outCipher != null)
|
||||
{
|
||||
byte[] data = outCipher.DoFinal();
|
||||
stream.Write(data, 0, data.Length);
|
||||
stream.Flush();
|
||||
}
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
// Note: outCipher.DoFinal is only called during Close()
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
public sealed override long Seek(
|
||||
long offset,
|
||||
SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed override void SetLength(
|
||||
long length)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
28
MinecraftClient/Crypto/Streams/BouncyAes/CryptoException.cs
Normal file
28
MinecraftClient/Crypto/Streams/BouncyAes/CryptoException.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
|
||||
[Serializable]
|
||||
#endif
|
||||
public class CryptoException
|
||||
: Exception
|
||||
{
|
||||
public CryptoException()
|
||||
{
|
||||
}
|
||||
|
||||
public CryptoException(
|
||||
string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public CryptoException(
|
||||
string message,
|
||||
Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
/**
|
||||
* this exception is thrown if a buffer that is meant to have output
|
||||
* copied into it turns out to be too short, or if we've been given
|
||||
* insufficient input. In general this exception will Get thrown rather
|
||||
* than an ArrayOutOfBounds exception.
|
||||
*/
|
||||
#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
|
||||
[Serializable]
|
||||
#endif
|
||||
public class DataLengthException
|
||||
: CryptoException
|
||||
{
|
||||
/**
|
||||
* base constructor.
|
||||
*/
|
||||
public DataLengthException()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* create a DataLengthException with the given message.
|
||||
*
|
||||
* @param message the message to be carried with the exception.
|
||||
*/
|
||||
public DataLengthException(
|
||||
string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DataLengthException(
|
||||
string message,
|
||||
Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
36
MinecraftClient/Crypto/Streams/BouncyAes/IBlockCipher.cs
Normal file
36
MinecraftClient/Crypto/Streams/BouncyAes/IBlockCipher.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
/// <remarks>Base interface for a symmetric key block cipher.</remarks>
|
||||
public interface IBlockCipher
|
||||
{
|
||||
/// <summary>The name of the algorithm this cipher implements.</summary>
|
||||
string AlgorithmName { get; }
|
||||
|
||||
/// <summary>Initialise the cipher.</summary>
|
||||
/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
|
||||
/// <param name="parameters">The key or other data required by the cipher.</param>
|
||||
void Init(bool forEncryption, ICipherParameters parameters);
|
||||
|
||||
/// <returns>The block size for this cipher, in bytes.</returns>
|
||||
int GetBlockSize();
|
||||
|
||||
/// <summary>Indicates whether this cipher can handle partial blocks.</summary>
|
||||
bool IsPartialBlockOkay { get; }
|
||||
|
||||
/// <summary>Process a block.</summary>
|
||||
/// <param name="inBuf">The input buffer.</param>
|
||||
/// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param>
|
||||
/// <param name="outBuf">The output buffer.</param>
|
||||
/// <param name="outOff">The offset into <paramref>outBuf</paramref> to write the output block.</param>
|
||||
/// <exception cref="DataLengthException">If input block is wrong size, or outBuf too small.</exception>
|
||||
/// <returns>The number of bytes processed and produced.</returns>
|
||||
int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff);
|
||||
|
||||
/// <summary>
|
||||
/// Reset the cipher to the same state as it was after the last init (if there was one).
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
44
MinecraftClient/Crypto/Streams/BouncyAes/IBufferedCipher.cs
Normal file
44
MinecraftClient/Crypto/Streams/BouncyAes/IBufferedCipher.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
/// <remarks>Block cipher engines are expected to conform to this interface.</remarks>
|
||||
public interface IBufferedCipher
|
||||
{
|
||||
/// <summary>The name of the algorithm this cipher implements.</summary>
|
||||
string AlgorithmName { get; }
|
||||
|
||||
/// <summary>Initialise the cipher.</summary>
|
||||
/// <param name="forEncryption">If true the cipher is initialised for encryption,
|
||||
/// if false for decryption.</param>
|
||||
/// <param name="parameters">The key and other data required by the cipher.</param>
|
||||
void Init(bool forEncryption, ICipherParameters parameters);
|
||||
|
||||
int GetBlockSize();
|
||||
|
||||
int GetOutputSize(int inputLen);
|
||||
|
||||
int GetUpdateOutputSize(int inputLen);
|
||||
|
||||
byte[] ProcessByte(byte input);
|
||||
int ProcessByte(byte input, byte[] output, int outOff);
|
||||
|
||||
byte[] ProcessBytes(byte[] input);
|
||||
byte[] ProcessBytes(byte[] input, int inOff, int length);
|
||||
int ProcessBytes(byte[] input, byte[] output, int outOff);
|
||||
int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff);
|
||||
|
||||
byte[] DoFinal();
|
||||
byte[] DoFinal(byte[] input);
|
||||
byte[] DoFinal(byte[] input, int inOff, int length);
|
||||
int DoFinal(byte[] output, int outOff);
|
||||
int DoFinal(byte[] input, byte[] output, int outOff);
|
||||
int DoFinal(byte[] input, int inOff, int length, byte[] output, int outOff);
|
||||
|
||||
/// <summary>
|
||||
/// Reset the cipher. After resetting the cipher is in the same state
|
||||
/// as it was after the last init (if there was one).
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
/**
|
||||
* all parameter classes implement this.
|
||||
*/
|
||||
public interface ICipherParameters
|
||||
{
|
||||
}
|
||||
}
|
||||
43
MinecraftClient/Crypto/Streams/BouncyAes/KeyParameter.cs
Normal file
43
MinecraftClient/Crypto/Streams/BouncyAes/KeyParameter.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
|
||||
using Org.BouncyCastle.Crypto;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Parameters
|
||||
{
|
||||
public class KeyParameter
|
||||
: ICipherParameters
|
||||
{
|
||||
private readonly byte[] key;
|
||||
|
||||
public KeyParameter(
|
||||
byte[] key)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
this.key = (byte[]) key.Clone();
|
||||
}
|
||||
|
||||
public KeyParameter(
|
||||
byte[] key,
|
||||
int keyOff,
|
||||
int keyLen)
|
||||
{
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
if (keyOff < 0 || keyOff > key.Length)
|
||||
throw new ArgumentOutOfRangeException("keyOff");
|
||||
if (keyLen < 0 || (keyOff + keyLen) > key.Length)
|
||||
throw new ArgumentOutOfRangeException("keyLen");
|
||||
|
||||
this.key = new byte[keyLen];
|
||||
Array.Copy(key, keyOff, this.key, 0, keyLen);
|
||||
}
|
||||
|
||||
public byte[] GetKey()
|
||||
{
|
||||
return (byte[]) key.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto
|
||||
{
|
||||
#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
|
||||
[Serializable]
|
||||
#endif
|
||||
public class OutputLengthException
|
||||
: DataLengthException
|
||||
{
|
||||
public OutputLengthException()
|
||||
{
|
||||
}
|
||||
|
||||
public OutputLengthException(
|
||||
string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public OutputLengthException(
|
||||
string message,
|
||||
Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
266
MinecraftClient/Crypto/Streams/BouncyAes/Pack.cs
Normal file
266
MinecraftClient/Crypto/Streams/BouncyAes/Pack.cs
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Utilities
|
||||
{
|
||||
internal sealed class Pack
|
||||
{
|
||||
private Pack()
|
||||
{
|
||||
}
|
||||
|
||||
internal static void UInt16_To_BE(ushort n, byte[] bs)
|
||||
{
|
||||
bs[0] = (byte)(n >> 8);
|
||||
bs[1] = (byte)(n);
|
||||
}
|
||||
|
||||
internal static void UInt16_To_BE(ushort n, byte[] bs, int off)
|
||||
{
|
||||
bs[off] = (byte)(n >> 8);
|
||||
bs[off + 1] = (byte)(n);
|
||||
}
|
||||
|
||||
internal static ushort BE_To_UInt16(byte[] bs)
|
||||
{
|
||||
uint n = (uint)bs[0] << 8
|
||||
| (uint)bs[1];
|
||||
return (ushort)n;
|
||||
}
|
||||
|
||||
internal static ushort BE_To_UInt16(byte[] bs, int off)
|
||||
{
|
||||
uint n = (uint)bs[off] << 8
|
||||
| (uint)bs[off + 1];
|
||||
return (ushort)n;
|
||||
}
|
||||
|
||||
internal static byte[] UInt32_To_BE(uint n)
|
||||
{
|
||||
byte[] bs = new byte[4];
|
||||
UInt32_To_BE(n, bs, 0);
|
||||
return bs;
|
||||
}
|
||||
|
||||
internal static void UInt32_To_BE(uint n, byte[] bs)
|
||||
{
|
||||
bs[0] = (byte)(n >> 24);
|
||||
bs[1] = (byte)(n >> 16);
|
||||
bs[2] = (byte)(n >> 8);
|
||||
bs[3] = (byte)(n);
|
||||
}
|
||||
|
||||
internal static void UInt32_To_BE(uint n, byte[] bs, int off)
|
||||
{
|
||||
bs[off] = (byte)(n >> 24);
|
||||
bs[off + 1] = (byte)(n >> 16);
|
||||
bs[off + 2] = (byte)(n >> 8);
|
||||
bs[off + 3] = (byte)(n);
|
||||
}
|
||||
|
||||
internal static byte[] UInt32_To_BE(uint[] ns)
|
||||
{
|
||||
byte[] bs = new byte[4 * ns.Length];
|
||||
UInt32_To_BE(ns, bs, 0);
|
||||
return bs;
|
||||
}
|
||||
|
||||
internal static void UInt32_To_BE(uint[] ns, byte[] bs, int off)
|
||||
{
|
||||
for (int i = 0; i < ns.Length; ++i)
|
||||
{
|
||||
UInt32_To_BE(ns[i], bs, off);
|
||||
off += 4;
|
||||
}
|
||||
}
|
||||
|
||||
internal static uint BE_To_UInt32(byte[] bs)
|
||||
{
|
||||
return (uint)bs[0] << 24
|
||||
| (uint)bs[1] << 16
|
||||
| (uint)bs[2] << 8
|
||||
| (uint)bs[3];
|
||||
}
|
||||
|
||||
internal static uint BE_To_UInt32(byte[] bs, int off)
|
||||
{
|
||||
return (uint)bs[off] << 24
|
||||
| (uint)bs[off + 1] << 16
|
||||
| (uint)bs[off + 2] << 8
|
||||
| (uint)bs[off + 3];
|
||||
}
|
||||
|
||||
internal static void BE_To_UInt32(byte[] bs, int off, uint[] ns)
|
||||
{
|
||||
for (int i = 0; i < ns.Length; ++i)
|
||||
{
|
||||
ns[i] = BE_To_UInt32(bs, off);
|
||||
off += 4;
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] UInt64_To_BE(ulong n)
|
||||
{
|
||||
byte[] bs = new byte[8];
|
||||
UInt64_To_BE(n, bs, 0);
|
||||
return bs;
|
||||
}
|
||||
|
||||
internal static void UInt64_To_BE(ulong n, byte[] bs)
|
||||
{
|
||||
UInt32_To_BE((uint)(n >> 32), bs);
|
||||
UInt32_To_BE((uint)(n), bs, 4);
|
||||
}
|
||||
|
||||
internal static void UInt64_To_BE(ulong n, byte[] bs, int off)
|
||||
{
|
||||
UInt32_To_BE((uint)(n >> 32), bs, off);
|
||||
UInt32_To_BE((uint)(n), bs, off + 4);
|
||||
}
|
||||
|
||||
internal static ulong BE_To_UInt64(byte[] bs)
|
||||
{
|
||||
uint hi = BE_To_UInt32(bs);
|
||||
uint lo = BE_To_UInt32(bs, 4);
|
||||
return ((ulong)hi << 32) | (ulong)lo;
|
||||
}
|
||||
|
||||
internal static ulong BE_To_UInt64(byte[] bs, int off)
|
||||
{
|
||||
uint hi = BE_To_UInt32(bs, off);
|
||||
uint lo = BE_To_UInt32(bs, off + 4);
|
||||
return ((ulong)hi << 32) | (ulong)lo;
|
||||
}
|
||||
|
||||
internal static void UInt16_To_LE(ushort n, byte[] bs)
|
||||
{
|
||||
bs[0] = (byte)(n);
|
||||
bs[1] = (byte)(n >> 8);
|
||||
}
|
||||
|
||||
internal static void UInt16_To_LE(ushort n, byte[] bs, int off)
|
||||
{
|
||||
bs[off] = (byte)(n);
|
||||
bs[off + 1] = (byte)(n >> 8);
|
||||
}
|
||||
|
||||
internal static ushort LE_To_UInt16(byte[] bs)
|
||||
{
|
||||
uint n = (uint)bs[0]
|
||||
| (uint)bs[1] << 8;
|
||||
return (ushort)n;
|
||||
}
|
||||
|
||||
internal static ushort LE_To_UInt16(byte[] bs, int off)
|
||||
{
|
||||
uint n = (uint)bs[off]
|
||||
| (uint)bs[off + 1] << 8;
|
||||
return (ushort)n;
|
||||
}
|
||||
|
||||
internal static byte[] UInt32_To_LE(uint n)
|
||||
{
|
||||
byte[] bs = new byte[4];
|
||||
UInt32_To_LE(n, bs, 0);
|
||||
return bs;
|
||||
}
|
||||
|
||||
internal static void UInt32_To_LE(uint n, byte[] bs)
|
||||
{
|
||||
bs[0] = (byte)(n);
|
||||
bs[1] = (byte)(n >> 8);
|
||||
bs[2] = (byte)(n >> 16);
|
||||
bs[3] = (byte)(n >> 24);
|
||||
}
|
||||
|
||||
internal static void UInt32_To_LE(uint n, byte[] bs, int off)
|
||||
{
|
||||
bs[off] = (byte)(n);
|
||||
bs[off + 1] = (byte)(n >> 8);
|
||||
bs[off + 2] = (byte)(n >> 16);
|
||||
bs[off + 3] = (byte)(n >> 24);
|
||||
}
|
||||
|
||||
internal static byte[] UInt32_To_LE(uint[] ns)
|
||||
{
|
||||
byte[] bs = new byte[4 * ns.Length];
|
||||
UInt32_To_LE(ns, bs, 0);
|
||||
return bs;
|
||||
}
|
||||
|
||||
internal static void UInt32_To_LE(uint[] ns, byte[] bs, int off)
|
||||
{
|
||||
for (int i = 0; i < ns.Length; ++i)
|
||||
{
|
||||
UInt32_To_LE(ns[i], bs, off);
|
||||
off += 4;
|
||||
}
|
||||
}
|
||||
|
||||
internal static uint LE_To_UInt32(byte[] bs)
|
||||
{
|
||||
return (uint)bs[0]
|
||||
| (uint)bs[1] << 8
|
||||
| (uint)bs[2] << 16
|
||||
| (uint)bs[3] << 24;
|
||||
}
|
||||
|
||||
internal static uint LE_To_UInt32(byte[] bs, int off)
|
||||
{
|
||||
return (uint)bs[off]
|
||||
| (uint)bs[off + 1] << 8
|
||||
| (uint)bs[off + 2] << 16
|
||||
| (uint)bs[off + 3] << 24;
|
||||
}
|
||||
|
||||
internal static void LE_To_UInt32(byte[] bs, int off, uint[] ns)
|
||||
{
|
||||
for (int i = 0; i < ns.Length; ++i)
|
||||
{
|
||||
ns[i] = LE_To_UInt32(bs, off);
|
||||
off += 4;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LE_To_UInt32(byte[] bs, int bOff, uint[] ns, int nOff, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
ns[nOff + i] = LE_To_UInt32(bs, bOff);
|
||||
bOff += 4;
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] UInt64_To_LE(ulong n)
|
||||
{
|
||||
byte[] bs = new byte[8];
|
||||
UInt64_To_LE(n, bs, 0);
|
||||
return bs;
|
||||
}
|
||||
|
||||
internal static void UInt64_To_LE(ulong n, byte[] bs)
|
||||
{
|
||||
UInt32_To_LE((uint)(n), bs);
|
||||
UInt32_To_LE((uint)(n >> 32), bs, 4);
|
||||
}
|
||||
|
||||
internal static void UInt64_To_LE(ulong n, byte[] bs, int off)
|
||||
{
|
||||
UInt32_To_LE((uint)(n), bs, off);
|
||||
UInt32_To_LE((uint)(n >> 32), bs, off + 4);
|
||||
}
|
||||
|
||||
internal static ulong LE_To_UInt64(byte[] bs)
|
||||
{
|
||||
uint lo = LE_To_UInt32(bs);
|
||||
uint hi = LE_To_UInt32(bs, 4);
|
||||
return ((ulong)hi << 32) | (ulong)lo;
|
||||
}
|
||||
|
||||
internal static ulong LE_To_UInt64(byte[] bs, int off)
|
||||
{
|
||||
uint lo = LE_To_UInt32(bs, off);
|
||||
uint hi = LE_To_UInt32(bs, off + 4);
|
||||
return ((ulong)hi << 32) | (ulong)lo;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
MinecraftClient/Crypto/Streams/BouncyAes/ParametersWithIV.cs
Normal file
43
MinecraftClient/Crypto/Streams/BouncyAes/ParametersWithIV.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Parameters
|
||||
{
|
||||
public class ParametersWithIV
|
||||
: ICipherParameters
|
||||
{
|
||||
private readonly ICipherParameters parameters;
|
||||
private readonly byte[] iv;
|
||||
|
||||
public ParametersWithIV(
|
||||
ICipherParameters parameters,
|
||||
byte[] iv)
|
||||
: this(parameters, iv, 0, iv.Length)
|
||||
{
|
||||
}
|
||||
|
||||
public ParametersWithIV(
|
||||
ICipherParameters parameters,
|
||||
byte[] iv,
|
||||
int ivOff,
|
||||
int ivLen)
|
||||
{
|
||||
// NOTE: 'parameters' may be null to imply key re-use
|
||||
if (iv == null)
|
||||
throw new ArgumentNullException("iv");
|
||||
|
||||
this.parameters = parameters;
|
||||
this.iv = new byte[ivLen];
|
||||
Array.Copy(iv, ivOff, this.iv, 0, ivLen);
|
||||
}
|
||||
|
||||
public byte[] GetIV()
|
||||
{
|
||||
return (byte[]) iv.Clone();
|
||||
}
|
||||
|
||||
public ICipherParameters Parameters
|
||||
{
|
||||
get { return parameters; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,29 +4,28 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Engines;
|
||||
using Org.BouncyCastle.Crypto.Modes;
|
||||
using Org.BouncyCastle.Crypto.IO;
|
||||
|
||||
namespace MinecraftClient.Crypto.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// An encrypted stream using AES, used for encrypting network data on the fly using AES.
|
||||
/// This is a mono-compatible adaptation which only sends and receive 16 bytes at a time, and manually transforms blocks.
|
||||
/// Data is cached before reaching the 128bits block size necessary for mono which is not CFB-8 compatible.
|
||||
/// This is a mono-compatible adaptation which uses AES engine from the BouncyCastle project.
|
||||
/// </summary>
|
||||
|
||||
public class MonoAesStream : Stream, IAesStream
|
||||
{
|
||||
IPaddingProvider pad;
|
||||
ICryptoTransform enc;
|
||||
ICryptoTransform dec;
|
||||
List<byte> dec_cache = new List<byte>();
|
||||
List<byte> tosend_cache = new List<byte>();
|
||||
public MonoAesStream(System.IO.Stream stream, byte[] key, IPaddingProvider provider)
|
||||
CipherStream cstream;
|
||||
public MonoAesStream(System.IO.Stream stream, byte[] key)
|
||||
{
|
||||
BaseStream = stream;
|
||||
RijndaelManaged aes = GenerateAES(key);
|
||||
enc = aes.CreateEncryptor();
|
||||
dec = aes.CreateDecryptor();
|
||||
pad = provider;
|
||||
BufferedBlockCipher enc = GenerateAES(key, true);
|
||||
BufferedBlockCipher dec = GenerateAES(key, false);
|
||||
cstream = new CipherStream(stream, dec, enc);
|
||||
}
|
||||
public System.IO.Stream BaseStream { get; set; }
|
||||
|
||||
|
|
@ -76,25 +75,7 @@ namespace MinecraftClient.Crypto.Streams
|
|||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
while (dec_cache.Count < count)
|
||||
{
|
||||
byte[] temp_in = new byte[16];
|
||||
byte[] temp_out = new byte[16];
|
||||
int read = 0;
|
||||
while (read < 16)
|
||||
read += BaseStream.Read(temp_in, read, 16 - read);
|
||||
dec.TransformBlock(temp_in, 0, 16, temp_out, 0);
|
||||
foreach (byte b in temp_out)
|
||||
dec_cache.Add(b);
|
||||
}
|
||||
|
||||
for (int i = offset; i - offset < count; i++)
|
||||
{
|
||||
buffer[i] = dec_cache[0];
|
||||
dec_cache.RemoveAt(0);
|
||||
}
|
||||
|
||||
return count;
|
||||
return cstream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
|
|
@ -114,35 +95,13 @@ namespace MinecraftClient.Crypto.Streams
|
|||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
for (int i = offset; i - offset < count; i++)
|
||||
tosend_cache.Add(buffer[i]);
|
||||
|
||||
if (tosend_cache.Count < 16)
|
||||
tosend_cache.AddRange(pad.getPaddingPacket());
|
||||
|
||||
while (tosend_cache.Count > 16)
|
||||
{
|
||||
byte[] temp_in = new byte[16];
|
||||
byte[] temp_out = new byte[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
temp_in[i] = tosend_cache[0];
|
||||
tosend_cache.RemoveAt(0);
|
||||
}
|
||||
enc.TransformBlock(temp_in, 0, 16, temp_out, 0);
|
||||
BaseStream.Write(temp_out, 0, 16);
|
||||
}
|
||||
cstream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
private RijndaelManaged GenerateAES(byte[] key)
|
||||
private BufferedBlockCipher GenerateAES(byte[] key, bool forEncryption)
|
||||
{
|
||||
RijndaelManaged cipher = new RijndaelManaged();
|
||||
cipher.Mode = CipherMode.CFB;
|
||||
cipher.Padding = PaddingMode.None;
|
||||
cipher.KeySize = 128;
|
||||
cipher.FeedbackSize = 8;
|
||||
cipher.Key = key;
|
||||
cipher.IV = key;
|
||||
BufferedBlockCipher cipher = new BufferedBlockCipher(new CfbBlockCipher(new AesFastEngine(), 8));
|
||||
cipher.Init(forEncryption, new ParametersWithIV(new KeyParameter(key), key));
|
||||
return cipher;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
95
MinecraftClient/Mapping/Block.cs
Normal file
95
MinecraftClient/Mapping/Block.cs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Minecraft Block
|
||||
/// </summary>
|
||||
public struct Block
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage for block ID and metadata
|
||||
/// </summary>
|
||||
private ushort blockIdAndMeta;
|
||||
|
||||
/// <summary>
|
||||
/// Id of the block
|
||||
/// </summary>
|
||||
public short BlockId
|
||||
{
|
||||
get
|
||||
{
|
||||
return (short)(blockIdAndMeta >> 4);
|
||||
}
|
||||
set
|
||||
{
|
||||
blockIdAndMeta = (ushort)(value << 4 | BlockMeta);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of the block
|
||||
/// </summary>
|
||||
public byte BlockMeta
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)(blockIdAndMeta & 0x0F);
|
||||
}
|
||||
set
|
||||
{
|
||||
blockIdAndMeta = (ushort)((blockIdAndMeta & ~0x0F) | (value & 0x0F));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material of the block
|
||||
/// </summary>
|
||||
public Material Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Material)BlockId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a block of the specified type and metadata
|
||||
/// </summary>
|
||||
/// <param name="type">Block type</param>
|
||||
/// <param name="metadata">Block metadata</param>
|
||||
public Block(short type, byte metadata = 0)
|
||||
{
|
||||
this.blockIdAndMeta = 0;
|
||||
this.BlockId = type;
|
||||
this.BlockMeta = metadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a block of the specified type and metadata
|
||||
/// </summary>
|
||||
/// <param name="typeAndMeta">Type and metadata packed in the same value</param>
|
||||
public Block(ushort typeAndMeta)
|
||||
{
|
||||
this.blockIdAndMeta = typeAndMeta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a block of the specified type and metadata
|
||||
/// </summary>
|
||||
/// <param name="type">Block type</param>
|
||||
public Block(Material type, byte metadata = 0)
|
||||
: this((short)type, metadata) { }
|
||||
|
||||
/// <summary>
|
||||
/// String representation of the block
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return BlockId.ToString() + (BlockMeta != 0 ? ":" + BlockMeta.ToString() : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
63
MinecraftClient/Mapping/Chunk.cs
Normal file
63
MinecraftClient/Mapping/Chunk.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a chunk of terrain in a Minecraft world
|
||||
/// </summary>
|
||||
public class Chunk
|
||||
{
|
||||
public const int SizeX = 16;
|
||||
public const int SizeY = 16;
|
||||
public const int SizeZ = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Blocks contained into the chunk
|
||||
/// </summary>
|
||||
private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ];
|
||||
|
||||
/// <summary>
|
||||
/// Read, or set the specified block
|
||||
/// </summary>
|
||||
/// <param name="blockX">Block X</param>
|
||||
/// <param name="blockY">Block Y</param>
|
||||
/// <param name="blockZ">Block Z</param>
|
||||
/// <returns>chunk at the given location</returns>
|
||||
public Block this[int blockX, int blockY, int blockZ]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (blockX < 0 || blockX >= SizeX)
|
||||
throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (SizeX - 1) + " (inclusive)");
|
||||
if (blockY < 0 || blockY >= SizeY)
|
||||
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
|
||||
if (blockZ < 0 || blockZ >= SizeZ)
|
||||
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||
return blocks[blockX, blockY, blockZ];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (blockX < 0 || blockX >= SizeX)
|
||||
throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (SizeX - 1) + " (inclusive)");
|
||||
if (blockY < 0 || blockY >= SizeY)
|
||||
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
|
||||
if (blockZ < 0 || blockZ >= SizeZ)
|
||||
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||
blocks[blockX, blockY, blockZ] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location, a modulo will be applied</param>
|
||||
/// <returns>The block</returns>
|
||||
public Block GetBlock(Location location)
|
||||
{
|
||||
return this[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ];
|
||||
}
|
||||
}
|
||||
}
|
||||
48
MinecraftClient/Mapping/ChunkColumn.cs
Normal file
48
MinecraftClient/Mapping/ChunkColumn.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a column of chunks of terrain in a Minecraft world
|
||||
/// </summary>
|
||||
public class ChunkColumn
|
||||
{
|
||||
public const int ColumnSize = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Blocks contained into the chunk
|
||||
/// </summary>
|
||||
private readonly Chunk[] chunks = new Chunk[ColumnSize];
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the specified chunk column
|
||||
/// </summary>
|
||||
/// <param name="chunkX">ChunkColumn X</param>
|
||||
/// <param name="chunkY">ChunkColumn Y</param>
|
||||
/// <returns>chunk at the given location</returns>
|
||||
public Chunk this[int chunkY]
|
||||
{
|
||||
get
|
||||
{
|
||||
return chunks[chunkY];
|
||||
}
|
||||
set
|
||||
{
|
||||
chunks[chunkY] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get chunk at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location, a modulo will be applied</param>
|
||||
/// <returns>The chunk, or null if not loaded</returns>
|
||||
public Chunk GetChunk(Location location)
|
||||
{
|
||||
return this[location.ChunkY];
|
||||
}
|
||||
}
|
||||
}
|
||||
21
MinecraftClient/Mapping/Direction.cs
Normal file
21
MinecraftClient/Mapping/Direction.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a unit movement in the world
|
||||
/// </summary>
|
||||
/// <see href="http://minecraft.gamepedia.com/Coordinates"/>
|
||||
public enum Direction
|
||||
{
|
||||
South = 0,
|
||||
West = 1,
|
||||
North = 2,
|
||||
East = 3,
|
||||
Up = 4,
|
||||
Down = 5
|
||||
}
|
||||
}
|
||||
331
MinecraftClient/Mapping/Location.cs
Normal file
331
MinecraftClient/Mapping/Location.cs
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a location into a Minecraft world
|
||||
/// </summary>
|
||||
public struct Location
|
||||
{
|
||||
/// <summary>
|
||||
/// The X Coordinate
|
||||
/// </summary>
|
||||
public double X;
|
||||
|
||||
/// <summary>
|
||||
/// The Y Coordinate (vertical)
|
||||
/// </summary>
|
||||
public double Y;
|
||||
|
||||
/// <summary>
|
||||
/// The Z coordinate
|
||||
/// </summary>
|
||||
public double Z;
|
||||
|
||||
/// <summary>
|
||||
/// Get location with zeroed coordinates
|
||||
/// </summary>
|
||||
public static Location Zero
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Location(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new location
|
||||
/// </summary>
|
||||
public Location(double x, double y, double z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new location
|
||||
/// </summary>
|
||||
/// <param name="chunkX">Location of the chunk into the world</param>
|
||||
/// <param name="chunkZ">Location of the chunk into the world</param>
|
||||
/// <param name="blockX">Location of the block into the chunk</param>
|
||||
/// <param name="blockY">Location of the block into the world</param>
|
||||
/// <param name="blockZ">Location of the block into the chunk</param>
|
||||
public Location(int chunkX, int chunkZ, int blockX, int blockY, int blockZ)
|
||||
{
|
||||
X = chunkX * Chunk.SizeX + blockX;
|
||||
Y = blockY;
|
||||
Z = chunkZ * Chunk.SizeZ + blockZ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X index of the corresponding chunk in the world
|
||||
/// </summary>
|
||||
public int ChunkX
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)Math.Floor(X / Chunk.SizeX);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y index of the corresponding chunk in the world
|
||||
/// </summary>
|
||||
public int ChunkY
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)Math.Floor(Y / Chunk.SizeY);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Z index of the corresponding chunk in the world
|
||||
/// </summary>
|
||||
public int ChunkZ
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)Math.Floor(Z / Chunk.SizeZ);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X index of the corresponding block in the corresponding chunk of the world
|
||||
/// </summary>
|
||||
public int ChunkBlockX
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Math.Floor(X) % Chunk.SizeX + Chunk.SizeX) % Chunk.SizeX;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y index of the corresponding block in the corresponding chunk of the world
|
||||
/// </summary>
|
||||
public int ChunkBlockY
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Math.Floor(Y) % Chunk.SizeY + Chunk.SizeY) % Chunk.SizeY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Z index of the corresponding block in the corresponding chunk of the world
|
||||
/// </summary>
|
||||
public int ChunkBlockZ
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Math.Floor(Z) % Chunk.SizeZ + Chunk.SizeZ) % Chunk.SizeZ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a squared distance to the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Other location for computing distance</param>
|
||||
/// <returns>Distance to the specified location, without using a square root</returns>
|
||||
public double DistanceSquared(Location location)
|
||||
{
|
||||
return ((X - location.X) * (X - location.X))
|
||||
+ ((Y - location.Y) * (Y - location.Y))
|
||||
+ ((Z - location.Z) * (Z - location.Z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get exact distance to the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Other location for computing distance</param>
|
||||
/// <returns>Distance to the specified location, with square root so lower performances</returns>
|
||||
public double Distance(Location location)
|
||||
{
|
||||
return Math.Sqrt(DistanceSquared(location));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to compare to</param>
|
||||
/// <returns>TRUE if the locations are equals</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj is Location)
|
||||
{
|
||||
return ((int)this.X) == ((int)((Location)obj).X)
|
||||
&& ((int)this.Y) == ((int)((Location)obj).Y)
|
||||
&& ((int)this.Z) == ((int)((Location)obj).Z);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a representation of the location as unsigned long
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A modulo will be applied if the location is outside the following ranges:
|
||||
/// X: -33,554,432 to +33,554,431
|
||||
/// Y: -2,048 to +2,047
|
||||
/// Z: -33,554,432 to +33,554,431
|
||||
/// </remarks>
|
||||
/// <returns>Location representation as ulong</returns>
|
||||
|
||||
public ulong GetLong()
|
||||
{
|
||||
return ((((ulong)X) & 0x3FFFFFF) << 38) | ((((ulong)Y) & 0xFFF) << 26) | (((ulong)Z) & 0x3FFFFFF);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a location from an unsigned long.
|
||||
/// </summary>
|
||||
/// <returns>Location represented by the ulong</returns>
|
||||
|
||||
public static Location FromLong(ulong location)
|
||||
{
|
||||
int x = (int)(location >> 38);
|
||||
int y = (int)((location >> 26) & 0xFFF);
|
||||
int z = (int)(location << 38 >> 38);
|
||||
if (x >= 33554432)
|
||||
x -= 67108864;
|
||||
if (y >= 2048)
|
||||
y -= 4096;
|
||||
if (z >= 33554432)
|
||||
z -= 67108864;
|
||||
return new Location(x, y, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
||||
/// </summary>
|
||||
/// <param name="loc1">First location to compare</param>
|
||||
/// <param name="loc2">Second location to compare</param>
|
||||
/// <returns>TRUE if the locations are equals</returns>
|
||||
public static bool operator ==(Location loc1, Location loc2)
|
||||
{
|
||||
if (loc1 == null && loc2 == null)
|
||||
return true;
|
||||
if (loc1 == null || loc2 == null)
|
||||
return false;
|
||||
return loc1.Equals(loc2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two locations. Locations are not equals if the integer part of their coordinates are not equals.
|
||||
/// </summary>
|
||||
/// <param name="loc1">First location to compare</param>
|
||||
/// <param name="loc2">Second location to compare</param>
|
||||
/// <returns>TRUE if the locations are equals</returns>
|
||||
public static bool operator !=(Location loc1, Location loc2)
|
||||
{
|
||||
if (loc1 == null && loc2 == null)
|
||||
return true;
|
||||
if (loc1 == null || loc2 == null)
|
||||
return false;
|
||||
return !loc1.Equals(loc2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sums two locations and returns the result.
|
||||
/// </summary>
|
||||
/// <exception cref="NullReferenceException">
|
||||
/// Thrown if one of the provided location is null
|
||||
/// </exception>
|
||||
/// <param name="loc1">First location to sum</param>
|
||||
/// <param name="loc2">Second location to sum</param>
|
||||
/// <returns>Sum of the two locations</returns>
|
||||
public static Location operator +(Location loc1, Location loc2)
|
||||
{
|
||||
return new Location
|
||||
(
|
||||
loc1.X + loc2.X,
|
||||
loc1.Y + loc2.Y,
|
||||
loc1.Z + loc2.Z
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Substract a location to another
|
||||
/// </summary>
|
||||
/// <exception cref="NullReferenceException">
|
||||
/// Thrown if one of the provided location is null
|
||||
/// </exception>
|
||||
/// <param name="loc1">First location</param>
|
||||
/// <param name="loc2">Location to substract to the first one</param>
|
||||
/// <returns>Sum of the two locations</returns>
|
||||
public static Location operator -(Location loc1, Location loc2)
|
||||
{
|
||||
return new Location
|
||||
(
|
||||
loc1.X - loc2.X,
|
||||
loc1.Y - loc2.Y,
|
||||
loc1.Z - loc2.Z
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply a location by a scalar value
|
||||
/// </summary>
|
||||
/// <param name="loc">Location to multiply</param>
|
||||
/// <param name="val">Scalar value</param>
|
||||
/// <returns>Product of the location and the scalar value</returns>
|
||||
public static Location operator *(Location loc, double val)
|
||||
{
|
||||
return new Location
|
||||
(
|
||||
loc.X * val,
|
||||
loc.Y * val,
|
||||
loc.Z * val
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divide a location by a scalar value
|
||||
/// </summary>
|
||||
/// <param name="loc">Location to divide</param>
|
||||
/// <param name="val">Scalar value</param>
|
||||
/// <returns>Result of the division</returns>
|
||||
public static Location operator /(Location loc, double val)
|
||||
{
|
||||
return new Location
|
||||
(
|
||||
loc.X / val,
|
||||
loc.Y / val,
|
||||
loc.Z / val
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DO NOT USE. Defined to comply with C# requirements requiring a GetHashCode() when overriding Equals() or ==
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A modulo will be applied if the location is outside the following ranges:
|
||||
/// X: -4096 to +4095
|
||||
/// Y: -32 to +31
|
||||
/// Z: -4096 to +4095
|
||||
/// </remarks>
|
||||
/// <returns>A simplified version of the location</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (((int)X) & ~((~0) << 13)) << 19
|
||||
| (((int)Y) & ~((~0) << 13)) << 13
|
||||
| (((int)Z) & ~((~0) << 06)) << 00;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the location into a string representation
|
||||
/// </summary>
|
||||
/// <returns>String representation of the location</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("X:{0} Y:{1} Z:{2}", X, Y, Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
365
MinecraftClient/Mapping/Material.cs
Normal file
365
MinecraftClient/Mapping/Material.cs
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents Minecraft Materials
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Mostly ported from CraftBukkit's Material class
|
||||
/// </remarks>
|
||||
/// <see href="https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/Material.java"/>
|
||||
public enum Material
|
||||
{
|
||||
Air = 0,
|
||||
Stone = 1,
|
||||
Grass = 2,
|
||||
Dirt = 3,
|
||||
Cobblestone = 4,
|
||||
Wood = 5,
|
||||
Sapling = 6,
|
||||
Bedrock = 7,
|
||||
Water = 8,
|
||||
StationaryWater = 9,
|
||||
Lava = 10,
|
||||
StationaryLava = 11,
|
||||
Sand = 12,
|
||||
Gravel = 13,
|
||||
GoldOre = 14,
|
||||
IronOre = 15,
|
||||
CoalOre = 16,
|
||||
Log = 17,
|
||||
Leaves = 18,
|
||||
Sponge = 19,
|
||||
Glass = 20,
|
||||
LapisOre = 21,
|
||||
LapisBlock = 22,
|
||||
Dispenser = 23,
|
||||
Sandstone = 24,
|
||||
NoteBlock = 25,
|
||||
BedBlock = 26,
|
||||
PoweredRail = 27,
|
||||
DetectorRail = 28,
|
||||
PistonStickyBase = 29,
|
||||
Web = 30,
|
||||
LongGrass = 31,
|
||||
DeadBush = 32,
|
||||
PistonBase = 33,
|
||||
PistonExtension = 34,
|
||||
Wool = 35,
|
||||
PistonMovingPiece = 36,
|
||||
YellowFlower = 37,
|
||||
RedRose = 38,
|
||||
BrownMushroom = 39,
|
||||
RedMushroom = 40,
|
||||
GoldBlock = 41,
|
||||
IronBlock = 42,
|
||||
DoubleStep = 43,
|
||||
Step = 44,
|
||||
Brick = 45,
|
||||
Tnt = 46,
|
||||
Bookshelf = 47,
|
||||
MossyCobblestone = 48,
|
||||
Obsidian = 49,
|
||||
Torch = 50,
|
||||
Fire = 51,
|
||||
MobSpawner = 52,
|
||||
WoodStairs = 53,
|
||||
Chest = 54,
|
||||
RedstoneWire = 55,
|
||||
DiamondOre = 56,
|
||||
DiamondBlock = 57,
|
||||
Workbench = 58,
|
||||
Crops = 59,
|
||||
Soil = 60,
|
||||
Furnace = 61,
|
||||
BurningFurnace = 62,
|
||||
SignPost = 63,
|
||||
WoodenDoor = 64,
|
||||
Ladder = 65,
|
||||
Rails = 66,
|
||||
CobblestoneStairs = 67,
|
||||
WallSign = 68,
|
||||
Lever = 69,
|
||||
StonePlate = 70,
|
||||
IronDoorBlock = 71,
|
||||
WoodPlate = 72,
|
||||
RedstoneOre = 73,
|
||||
GlowingRedstoneOre = 74,
|
||||
RedstoneTorchOff = 75,
|
||||
RedstoneTorchOn = 76,
|
||||
StoneButton = 77,
|
||||
Snow = 78,
|
||||
Ice = 79,
|
||||
SnowBlock = 80,
|
||||
Cactus = 81,
|
||||
Clay = 82,
|
||||
SugarCaneBlock = 83,
|
||||
Jukebox = 84,
|
||||
Fence = 85,
|
||||
Pumpkin = 86,
|
||||
Netherrack = 87,
|
||||
SoulSand = 88,
|
||||
Glowstone = 89,
|
||||
Portal = 90,
|
||||
JackOLantern = 91,
|
||||
CakeBlock = 92,
|
||||
DiodeBlockOff = 93,
|
||||
DiodeBlockOn = 94,
|
||||
StainedGlass = 95,
|
||||
TrapDoor = 96,
|
||||
MonsterEggs = 97,
|
||||
SmoothBrick = 98,
|
||||
HugeMushroom1 = 99,
|
||||
HugeMushroom2 = 100,
|
||||
IronFence = 101,
|
||||
ThinGlass = 102,
|
||||
MelonBlock = 103,
|
||||
PumpkinStem = 104,
|
||||
MelonStem = 105,
|
||||
Vine = 106,
|
||||
FenceGate = 107,
|
||||
BrickStairs = 108,
|
||||
SmoothStairs = 109,
|
||||
Mycel = 110,
|
||||
WaterLily = 111,
|
||||
NetherBrick = 112,
|
||||
NetherFence = 113,
|
||||
NetherBrickStairs = 114,
|
||||
NetherWarts = 115,
|
||||
EnchantmentTable = 116,
|
||||
BrewingStand = 117,
|
||||
Cauldron = 118,
|
||||
EnderPortal = 119,
|
||||
EnderPortalFrame = 120,
|
||||
EnderStone = 121,
|
||||
DragonEgg = 122,
|
||||
RedstoneLampOff = 123,
|
||||
RedstoneLampOn = 124,
|
||||
WoodDoubleStep = 125,
|
||||
WoodStep = 126,
|
||||
Cocoa = 127,
|
||||
SandstoneStairs = 128,
|
||||
EmeraldOre = 129,
|
||||
EnderChest = 130,
|
||||
TripwireHook = 131,
|
||||
Tripwire = 132,
|
||||
EmeraldBlock = 133,
|
||||
SpruceWoodStairs = 134,
|
||||
BirchWoodStairs = 135,
|
||||
JungleWoodStairs = 136,
|
||||
Command = 137,
|
||||
Beacon = 138,
|
||||
CobbleWall = 139,
|
||||
FlowerPot = 140,
|
||||
Carrot = 141,
|
||||
Potato = 142,
|
||||
WoodButton = 143,
|
||||
Skull = 144,
|
||||
Anvil = 145,
|
||||
TrappedChest = 146,
|
||||
GoldPlate = 147,
|
||||
IronPlate = 148,
|
||||
RedstoneComparatorOff = 149,
|
||||
RedstoneComparatorOn = 150,
|
||||
DaylightDetector = 151,
|
||||
RedstoneBlock = 152,
|
||||
QuartzOre = 153,
|
||||
Hopper = 154,
|
||||
QuartzBlock = 155,
|
||||
QuartzStairs = 156,
|
||||
ActivatorRail = 157,
|
||||
Dropper = 158,
|
||||
StainedClay = 159,
|
||||
StainedGlassPane = 160,
|
||||
Leaves2 = 161,
|
||||
Log2 = 162,
|
||||
AcaciaStairs = 163,
|
||||
DarkOakStairs = 164,
|
||||
HayBlock = 170,
|
||||
Carpet = 171,
|
||||
HardClay = 172,
|
||||
CoalBlock = 173,
|
||||
PackedIce = 174,
|
||||
DoublePlant = 175
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines extension methods for the Material enumeration
|
||||
/// </summary>
|
||||
public static class MaterialExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if the player cannot pass through the specified material
|
||||
/// </summary>
|
||||
/// <param name="m">Material to test</param>
|
||||
/// <returns>True if the material is harmful</returns>
|
||||
public static bool IsSolid(this Material m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case Material.Stone:
|
||||
case Material.Grass:
|
||||
case Material.Dirt:
|
||||
case Material.Cobblestone:
|
||||
case Material.Wood:
|
||||
case Material.Bedrock:
|
||||
case Material.Sand:
|
||||
case Material.Gravel:
|
||||
case Material.GoldOre:
|
||||
case Material.IronOre:
|
||||
case Material.CoalOre:
|
||||
case Material.Log:
|
||||
case Material.Leaves:
|
||||
case Material.Sponge:
|
||||
case Material.Glass:
|
||||
case Material.LapisOre:
|
||||
case Material.LapisBlock:
|
||||
case Material.Dispenser:
|
||||
case Material.Sandstone:
|
||||
case Material.NoteBlock:
|
||||
case Material.BedBlock:
|
||||
case Material.PistonStickyBase:
|
||||
case Material.PistonBase:
|
||||
case Material.PistonExtension:
|
||||
case Material.Wool:
|
||||
case Material.PistonMovingPiece:
|
||||
case Material.GoldBlock:
|
||||
case Material.IronBlock:
|
||||
case Material.DoubleStep:
|
||||
case Material.Step:
|
||||
case Material.Brick:
|
||||
case Material.Tnt:
|
||||
case Material.Bookshelf:
|
||||
case Material.MossyCobblestone:
|
||||
case Material.Obsidian:
|
||||
case Material.MobSpawner:
|
||||
case Material.WoodStairs:
|
||||
case Material.Chest:
|
||||
case Material.DiamondOre:
|
||||
case Material.DiamondBlock:
|
||||
case Material.Workbench:
|
||||
case Material.Soil:
|
||||
case Material.Furnace:
|
||||
case Material.BurningFurnace:
|
||||
case Material.SignPost:
|
||||
case Material.WoodenDoor:
|
||||
case Material.CobblestoneStairs:
|
||||
case Material.WallSign:
|
||||
case Material.StonePlate:
|
||||
case Material.IronDoorBlock:
|
||||
case Material.WoodPlate:
|
||||
case Material.RedstoneOre:
|
||||
case Material.GlowingRedstoneOre:
|
||||
case Material.Ice:
|
||||
case Material.SnowBlock:
|
||||
case Material.Cactus:
|
||||
case Material.Clay:
|
||||
case Material.Jukebox:
|
||||
case Material.Fence:
|
||||
case Material.Pumpkin:
|
||||
case Material.Netherrack:
|
||||
case Material.SoulSand:
|
||||
case Material.Glowstone:
|
||||
case Material.JackOLantern:
|
||||
case Material.CakeBlock:
|
||||
case Material.StainedGlass:
|
||||
case Material.TrapDoor:
|
||||
case Material.MonsterEggs:
|
||||
case Material.SmoothBrick:
|
||||
case Material.HugeMushroom1:
|
||||
case Material.HugeMushroom2:
|
||||
case Material.IronFence:
|
||||
case Material.ThinGlass:
|
||||
case Material.MelonBlock:
|
||||
case Material.FenceGate:
|
||||
case Material.BrickStairs:
|
||||
case Material.SmoothStairs:
|
||||
case Material.Mycel:
|
||||
case Material.NetherBrick:
|
||||
case Material.NetherFence:
|
||||
case Material.NetherBrickStairs:
|
||||
case Material.EnchantmentTable:
|
||||
case Material.BrewingStand:
|
||||
case Material.Cauldron:
|
||||
case Material.EnderPortalFrame:
|
||||
case Material.EnderStone:
|
||||
case Material.DragonEgg:
|
||||
case Material.RedstoneLampOff:
|
||||
case Material.RedstoneLampOn:
|
||||
case Material.WoodDoubleStep:
|
||||
case Material.WoodStep:
|
||||
case Material.SandstoneStairs:
|
||||
case Material.EmeraldOre:
|
||||
case Material.EnderChest:
|
||||
case Material.EmeraldBlock:
|
||||
case Material.SpruceWoodStairs:
|
||||
case Material.BirchWoodStairs:
|
||||
case Material.JungleWoodStairs:
|
||||
case Material.Command:
|
||||
case Material.Beacon:
|
||||
case Material.CobbleWall:
|
||||
case Material.Anvil:
|
||||
case Material.TrappedChest:
|
||||
case Material.GoldPlate:
|
||||
case Material.IronPlate:
|
||||
case Material.DaylightDetector:
|
||||
case Material.RedstoneBlock:
|
||||
case Material.QuartzOre:
|
||||
case Material.Hopper:
|
||||
case Material.QuartzBlock:
|
||||
case Material.QuartzStairs:
|
||||
case Material.Dropper:
|
||||
case Material.StainedClay:
|
||||
case Material.HayBlock:
|
||||
case Material.HardClay:
|
||||
case Material.CoalBlock:
|
||||
case Material.StainedGlassPane:
|
||||
case Material.Leaves2:
|
||||
case Material.Log2:
|
||||
case Material.AcaciaStairs:
|
||||
case Material.DarkOakStairs:
|
||||
case Material.PackedIce:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if contact with the provided material can harm players
|
||||
/// </summary>
|
||||
/// <param name="m">Material to test</param>
|
||||
/// <returns>True if the material is harmful</returns>
|
||||
public static bool CanHarmPlayers(this Material m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case Material.Fire:
|
||||
case Material.Cactus:
|
||||
case Material.Lava:
|
||||
case Material.StationaryLava:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the provided material is a liquid a player can swim into
|
||||
/// </summary>
|
||||
/// <param name="m">Material to test</param>
|
||||
/// <returns>True if the material is a liquid</returns>
|
||||
public static bool IsLiquid(this Material m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case Material.Water:
|
||||
case Material.StationaryWater:
|
||||
case Material.Lava:
|
||||
case Material.StationaryLava:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
270
MinecraftClient/Mapping/Movement.cs
Normal file
270
MinecraftClient/Mapping/Movement.cs
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows moving through a Minecraft world
|
||||
/// </summary>
|
||||
public static class Movement
|
||||
{
|
||||
/* ========= PATHFINDING METHODS ========= */
|
||||
|
||||
/// <summary>
|
||||
/// Handle movements due to gravity
|
||||
/// </summary>
|
||||
/// <param name="world">World the player is currently located in</param>
|
||||
/// <param name="location">Location the player is currently at</param>
|
||||
/// <returns>Updated location after applying gravity</returns>
|
||||
public static Location HandleGravity(World world, Location location)
|
||||
{
|
||||
Location onFoots = new Location(location.X, Math.Floor(location.Y), location.Z);
|
||||
Location belowFoots = Move(location, Direction.Down);
|
||||
if (!IsOnGround(world, location) && !IsSwimming(world, location))
|
||||
location = Move2Steps(location, belowFoots).Dequeue();
|
||||
else if (!(world.GetBlock(onFoots).Type.IsSolid()))
|
||||
location = Move2Steps(location, onFoots).Dequeue();
|
||||
return location;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of possible moves for the player
|
||||
/// </summary>
|
||||
/// <param name="world">World the player is currently located in</param>
|
||||
/// <param name="location">Location the player is currently at</param>
|
||||
/// <param name="allowUnsafe">Allow possible but unsafe locations</param>
|
||||
/// <returns>A list of new locations the player can move to</returns>
|
||||
public static IEnumerable<Location> GetAvailableMoves(World world, Location location, bool allowUnsafe = false)
|
||||
{
|
||||
List<Location> availableMoves = new List<Location>();
|
||||
if (IsOnGround(world, location) || IsSwimming(world, location))
|
||||
{
|
||||
foreach (Direction dir in Enum.GetValues(typeof(Direction)))
|
||||
if (CanMove(world, location, dir) && (allowUnsafe || IsSafe(world, Move(location, dir))))
|
||||
availableMoves.Add(Move(location, dir));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Direction dir in new []{ Direction.East, Direction.West, Direction.North, Direction.South })
|
||||
if (CanMove(world, location, dir) && IsOnGround(world, Move(location, dir)) && (allowUnsafe || IsSafe(world, Move(location, dir))))
|
||||
availableMoves.Add(Move(location, dir));
|
||||
availableMoves.Add(Move(location, Direction.Down));
|
||||
}
|
||||
return availableMoves;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompose a single move from a block to another into several steps
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Allows moving by little steps instead or directly moving between blocks,
|
||||
/// which would be rejected by anti-cheat plugins anyway.
|
||||
/// </remarks>
|
||||
/// <param name="start">Start location</param>
|
||||
/// <param name="goal">Destination location</param>
|
||||
/// <param name="stepsByBlock">Amount of steps by block</param>
|
||||
/// <returns>A list of locations corresponding to the requested steps</returns>
|
||||
public static Queue<Location> Move2Steps(Location start, Location goal, int stepsByBlock = 8)
|
||||
{
|
||||
if (stepsByBlock <= 0)
|
||||
stepsByBlock = 1;
|
||||
|
||||
double totalStepsDouble = start.Distance(goal) * stepsByBlock;
|
||||
int totalSteps = (int)Math.Ceiling(totalStepsDouble);
|
||||
Location step = (goal - start) / totalSteps;
|
||||
|
||||
if (totalStepsDouble >= 1)
|
||||
{
|
||||
Queue<Location> movementSteps = new Queue<Location>();
|
||||
for (int i = 1; i <= totalSteps; i++)
|
||||
movementSteps.Enqueue(start + step * i);
|
||||
return movementSteps;
|
||||
}
|
||||
else return new Queue<Location>(new[] { goal });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate a path from the start location to the destination location
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Based on the A* pathfinding algorithm described on Wikipedia
|
||||
/// </remarks>
|
||||
/// <see href="https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode"/>
|
||||
/// <param name="start">Start location</param>
|
||||
/// <param name="goal">Destination location</param>
|
||||
/// <param name="allowUnsafe">Allow possible but unsafe locations</param>
|
||||
/// <returns>A list of locations, or null if calculation failed</returns>
|
||||
public static Queue<Location> CalculatePath(World world, Location start, Location goal, bool allowUnsafe = false)
|
||||
{
|
||||
Queue<Location> result = null;
|
||||
|
||||
AutoTimeout.Perform(() =>
|
||||
{
|
||||
HashSet<Location> ClosedSet = new HashSet<Location>(); // The set of locations already evaluated.
|
||||
HashSet<Location> OpenSet = new HashSet<Location>(new[] { start }); // The set of tentative nodes to be evaluated, initially containing the start node
|
||||
Dictionary<Location, Location> Came_From = new Dictionary<Location, Location>(); // The map of navigated nodes.
|
||||
|
||||
Dictionary<Location, int> g_score = new Dictionary<Location, int>(); //:= map with default value of Infinity
|
||||
g_score[start] = 0; // Cost from start along best known path.
|
||||
// Estimated total cost from start to goal through y.
|
||||
Dictionary<Location, int> f_score = new Dictionary<Location, int>(); //:= map with default value of Infinity
|
||||
f_score[start] = (int)start.DistanceSquared(goal); //heuristic_cost_estimate(start, goal)
|
||||
|
||||
while (OpenSet.Count > 0)
|
||||
{
|
||||
Location current = //the node in OpenSet having the lowest f_score[] value
|
||||
OpenSet.Select(location => f_score.ContainsKey(location)
|
||||
? new KeyValuePair<Location, int>(location, f_score[location])
|
||||
: new KeyValuePair<Location, int>(location, int.MaxValue))
|
||||
.OrderBy(pair => pair.Value).First().Key;
|
||||
if (current == goal)
|
||||
{ //reconstruct_path(Came_From, goal)
|
||||
List<Location> total_path = new List<Location>(new[] { current });
|
||||
while (Came_From.ContainsKey(current))
|
||||
{
|
||||
current = Came_From[current];
|
||||
total_path.Add(current);
|
||||
}
|
||||
total_path.Reverse();
|
||||
result = new Queue<Location>(total_path);
|
||||
}
|
||||
OpenSet.Remove(current);
|
||||
ClosedSet.Add(current);
|
||||
foreach (Location neighbor in GetAvailableMoves(world, current, allowUnsafe))
|
||||
{
|
||||
if (ClosedSet.Contains(neighbor))
|
||||
continue; // Ignore the neighbor which is already evaluated.
|
||||
int tentative_g_score = g_score[current] + (int)current.DistanceSquared(neighbor); //dist_between(current,neighbor) // length of this path.
|
||||
if (!OpenSet.Contains(neighbor)) // Discover a new node
|
||||
OpenSet.Add(neighbor);
|
||||
else if (tentative_g_score >= g_score[neighbor])
|
||||
continue; // This is not a better path.
|
||||
|
||||
// This path is the best until now. Record it!
|
||||
Came_From[neighbor] = current;
|
||||
g_score[neighbor] = tentative_g_score;
|
||||
f_score[neighbor] = g_score[neighbor] + (int)neighbor.DistanceSquared(goal); //heuristic_cost_estimate(neighbor, goal)
|
||||
}
|
||||
}
|
||||
}, TimeSpan.FromSeconds(5));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ========= LOCATION PROPERTIES ========= */
|
||||
|
||||
/// <summary>
|
||||
/// Check if the specified location is on the ground
|
||||
/// </summary>
|
||||
/// <param name="world">World for performing check</param>
|
||||
/// <param name="location">Location to check</param>
|
||||
/// <returns>True if the specified location is on the ground</returns>
|
||||
public static bool IsOnGround(World world, Location location)
|
||||
{
|
||||
return world.GetBlock(Move(location, Direction.Down)).Type.IsSolid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the specified location implies swimming
|
||||
/// </summary>
|
||||
/// <param name="world">World for performing check</param>
|
||||
/// <param name="location">Location to check</param>
|
||||
/// <returns>True if the specified location implies swimming</returns>
|
||||
public static bool IsSwimming(World world, Location location)
|
||||
{
|
||||
return world.GetBlock(location).Type.IsLiquid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the specified location is safe
|
||||
/// </summary>
|
||||
/// <param name="world">World for performing check</param>
|
||||
/// <param name="location">Location to check</param>
|
||||
/// <returns>True if the destination location won't directly harm the player</returns>
|
||||
public static bool IsSafe(World world, Location location)
|
||||
{
|
||||
return
|
||||
//No block that can harm the player
|
||||
!world.GetBlock(location).Type.CanHarmPlayers()
|
||||
&& !world.GetBlock(Move(location, Direction.Up)).Type.CanHarmPlayers()
|
||||
&& !world.GetBlock(Move(location, Direction.Down)).Type.CanHarmPlayers()
|
||||
|
||||
//No fall from a too high place
|
||||
&& (world.GetBlock(Move(location, Direction.Down)).Type.IsSolid()
|
||||
|| world.GetBlock(Move(location, Direction.Down, 2)).Type.IsSolid()
|
||||
|| world.GetBlock(Move(location, Direction.Down, 3)).Type.IsSolid())
|
||||
|
||||
//Not an underwater location
|
||||
&& !(world.GetBlock(Move(location, Direction.Up)).Type.IsLiquid());
|
||||
}
|
||||
|
||||
/* ========= SIMPLE MOVEMENTS ========= */
|
||||
|
||||
/// <summary>
|
||||
/// Check if the player can move in the specified direction
|
||||
/// </summary>
|
||||
/// <param name="world">World the player is currently located in</param>
|
||||
/// <param name="location">Location the player is currently at</param>
|
||||
/// <param name="direction">Direction the player is moving to</param>
|
||||
/// <returns>True if the player can move in the specified direction</returns>
|
||||
public static bool CanMove(World world, Location location, Direction direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case Direction.Down:
|
||||
return !IsOnGround(world, location);
|
||||
case Direction.Up:
|
||||
return (IsOnGround(world, location) || IsSwimming(world, location))
|
||||
&& !world.GetBlock(Move(Move(location, Direction.Up), Direction.Up)).Type.IsSolid();
|
||||
case Direction.East:
|
||||
case Direction.West:
|
||||
case Direction.South:
|
||||
case Direction.North:
|
||||
return !world.GetBlock(Move(location, direction)).Type.IsSolid()
|
||||
&& !world.GetBlock(Move(Move(location, direction), Direction.Up)).Type.IsSolid();
|
||||
default:
|
||||
throw new ArgumentException("Unknown direction", "direction");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an updated location for moving in the specified direction
|
||||
/// </summary>
|
||||
/// <param name="location">Current location</param>
|
||||
/// <param name="direction">Direction to move to</param>
|
||||
/// <param name="length">Distance, in blocks</param>
|
||||
/// <returns>Updated location</returns>
|
||||
public static Location Move(Location location, Direction direction, int length = 1)
|
||||
{
|
||||
return location + Move(direction) * length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a location delta for moving in the specified direction
|
||||
/// </summary>
|
||||
/// <param name="direction">Direction to move to</param>
|
||||
/// <returns>A location delta for moving in that direction</returns>
|
||||
public static Location Move(Direction direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case Direction.Down:
|
||||
return new Location(0, -1, 0);
|
||||
case Direction.Up:
|
||||
return new Location(0, 1, 0);
|
||||
case Direction.East:
|
||||
return new Location(1, 0, 0);
|
||||
case Direction.West:
|
||||
return new Location(-1, 0, 0);
|
||||
case Direction.South:
|
||||
return new Location(0, 0, 1);
|
||||
case Direction.North:
|
||||
return new Location(0, 0, -1);
|
||||
default:
|
||||
throw new ArgumentException("Unknown direction", "direction");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
103
MinecraftClient/Mapping/World.cs
Normal file
103
MinecraftClient/Mapping/World.cs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Minecraft World
|
||||
/// </summary>
|
||||
public class World
|
||||
{
|
||||
/// <summary>
|
||||
/// The chunks contained into the Minecraft world
|
||||
/// </summary>
|
||||
private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>();
|
||||
|
||||
/// <summary>
|
||||
/// Read, set or unload the specified chunk column
|
||||
/// </summary>
|
||||
/// <param name="chunkX">ChunkColumn X</param>
|
||||
/// <param name="chunkY">ChunkColumn Y</param>
|
||||
/// <returns>chunk at the given location</returns>
|
||||
public ChunkColumn this[int chunkX, int chunkZ]
|
||||
{
|
||||
get
|
||||
{
|
||||
//Read a chunk
|
||||
if (chunks.ContainsKey(chunkX))
|
||||
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||
return chunks[chunkX][chunkZ];
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
//Update a chunk column
|
||||
if (!chunks.ContainsKey(chunkX))
|
||||
chunks[chunkX] = new Dictionary<int, ChunkColumn>();
|
||||
chunks[chunkX][chunkZ] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Unload a chunk column
|
||||
if (chunks.ContainsKey(chunkX))
|
||||
{
|
||||
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||
{
|
||||
chunks[chunkX].Remove(chunkZ);
|
||||
if (chunks[chunkX].Count == 0)
|
||||
chunks.Remove(chunkX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get chunk column at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to retrieve chunk column</param>
|
||||
/// <returns>The chunk column</returns>
|
||||
public ChunkColumn GetChunkColumn(Location location)
|
||||
{
|
||||
return this[location.ChunkX, location.ChunkZ];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to retrieve block from</param>
|
||||
/// <returns>Block at specified location or Air if the location is not loaded</returns>
|
||||
public Block GetBlock(Location location)
|
||||
{
|
||||
ChunkColumn column = GetChunkColumn(location);
|
||||
if (column != null)
|
||||
{
|
||||
Chunk chunk = column.GetChunk(location);
|
||||
if (chunk != null)
|
||||
return chunk.GetBlock(location);
|
||||
}
|
||||
return new Block(Material.Air);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set block at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to set block to</param>
|
||||
/// <param name="block">Block to set</param>
|
||||
public void SetBlock(Location location, Block block)
|
||||
{
|
||||
ChunkColumn column = this[location.ChunkX, location.ChunkZ];
|
||||
if (column != null)
|
||||
{
|
||||
Chunk chunk = column[location.ChunkY];
|
||||
if (chunk == null)
|
||||
column[location.ChunkY] = chunk = new Chunk();
|
||||
chunk[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ using System.IO;
|
|||
using System.Net;
|
||||
using MinecraftClient.Protocol;
|
||||
using MinecraftClient.Proxy;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
|
|
@ -17,16 +19,45 @@ namespace MinecraftClient
|
|||
|
||||
public class McTcpClient : IMinecraftComHandler
|
||||
{
|
||||
private static List<string> cmd_names = new List<string>();
|
||||
private static Dictionary<string, Command> cmds = new Dictionary<string, Command>();
|
||||
private List<ChatBot> bots = new List<ChatBot>();
|
||||
private Dictionary<Guid, string> onlinePlayers = new Dictionary<Guid,string>();
|
||||
private static List<ChatBots.Script> scripts_on_hold = new List<ChatBots.Script>();
|
||||
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 static int ReconnectionAttemptsLeft = 0;
|
||||
|
||||
private static readonly List<string> cmd_names = new List<string>();
|
||||
private static readonly Dictionary<string, Command> cmds = new Dictionary<string, Command>();
|
||||
private readonly Dictionary<Guid, string> onlinePlayers = new Dictionary<Guid, string>();
|
||||
|
||||
private readonly List<ChatBot> bots = new List<ChatBot>();
|
||||
private static readonly List<ChatBots.Script> scripts_on_hold = new List<ChatBots.Script>();
|
||||
public void BotLoad(ChatBot b) {
|
||||
b.SetHandler(this);
|
||||
bots.Add(b);
|
||||
b.Initialize();
|
||||
if (this.handler != null)
|
||||
{
|
||||
b.AfterGameJoined();
|
||||
}
|
||||
Settings.SingleCommand = "";
|
||||
}
|
||||
public void BotUnLoad(ChatBot b) {
|
||||
bots.RemoveAll(item => object.ReferenceEquals(item, b));
|
||||
|
||||
// ToList is needed to avoid an InvalidOperationException from modfiying the list while it's being iterated upon.
|
||||
var botRegistrations = registeredBotPluginChannels.Where(entry => entry.Value.Contains(b)).ToList();
|
||||
foreach (var entry in botRegistrations)
|
||||
{
|
||||
UnregisterPluginChannel(entry.Key, b);
|
||||
}
|
||||
}
|
||||
public void BotClear() { bots.Clear(); }
|
||||
|
||||
public static int AttemptsLeft = 0;
|
||||
private readonly Dictionary<string, List<ChatBot>> registeredBotPluginChannels = new Dictionary<string, List<ChatBot>>();
|
||||
private readonly List<string> registeredServerPluginChannels = new List<String>();
|
||||
|
||||
private object locationLock = new object();
|
||||
private bool locationReceived = false;
|
||||
private World world = new World();
|
||||
private Queue<Location> steps;
|
||||
private Queue<Location> path;
|
||||
private Location location;
|
||||
|
||||
private string host;
|
||||
private int port;
|
||||
|
|
@ -34,12 +65,14 @@ namespace MinecraftClient
|
|||
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; }
|
||||
|
||||
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; }
|
||||
public Location GetCurrentLocation() { return location; }
|
||||
public World GetWorld() { return world; }
|
||||
|
||||
TcpClient client;
|
||||
IMinecraftCom handler;
|
||||
Thread cmdprompt;
|
||||
|
|
@ -54,9 +87,9 @@ namespace MinecraftClient
|
|||
/// <param name="port">The server port to use</param>
|
||||
/// <param name="protocolversion">Minecraft protocol version to use</param>
|
||||
|
||||
public McTcpClient(string username, string uuid, string sessionID, int protocolversion, string server_ip, ushort port)
|
||||
public McTcpClient(string username, string uuid, string sessionID, int protocolversion, ForgeInfo forgeInfo, string server_ip, ushort port)
|
||||
{
|
||||
StartClient(username, uuid, sessionID, server_ip, port, protocolversion, false, "");
|
||||
StartClient(username, uuid, sessionID, server_ip, port, protocolversion, forgeInfo, false, "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -70,9 +103,9 @@ namespace MinecraftClient
|
|||
/// <param name="protocolversion">Minecraft protocol version to use</param>
|
||||
/// <param name="command">The text or command to send.</param>
|
||||
|
||||
public McTcpClient(string username, string uuid, string sessionID, string server_ip, ushort port, int protocolversion, string command)
|
||||
public McTcpClient(string username, string uuid, string sessionID, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, string command)
|
||||
{
|
||||
StartClient(username, uuid, sessionID, server_ip, port, protocolversion, true, command);
|
||||
StartClient(username, uuid, sessionID, server_ip, port, protocolversion, forgeInfo, true, command);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -87,8 +120,9 @@ namespace MinecraftClient
|
|||
/// <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 uuid, string sessionID, string server_ip, ushort port, int protocolversion, bool singlecommand, string command)
|
||||
private void StartClient(string user, string uuid, string sessionID, string server_ip, ushort port, int protocolversion, ForgeInfo forgeInfo, bool singlecommand, string command)
|
||||
{
|
||||
bool retry = false;
|
||||
this.sessionid = sessionID;
|
||||
this.uuid = uuid;
|
||||
this.username = user;
|
||||
|
|
@ -100,56 +134,75 @@ namespace MinecraftClient
|
|||
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.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.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);
|
||||
handler = Protocol.ProtocolHandler.getProtocolHandler(client, protocolversion, forgeInfo, this);
|
||||
Console.WriteLine("Version is supported.\nLogging in...");
|
||||
|
||||
if (handler.Login())
|
||||
|
||||
try
|
||||
{
|
||||
if (singlecommand)
|
||||
if (handler.Login())
|
||||
{
|
||||
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();
|
||||
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)
|
||||
catch (SocketException e)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8" + e.Message);
|
||||
Console.WriteLine("Failed to connect to this IP.");
|
||||
if (AttemptsLeft > 0)
|
||||
retry = true;
|
||||
}
|
||||
|
||||
if (retry)
|
||||
{
|
||||
if (ReconnectionAttemptsLeft > 0)
|
||||
{
|
||||
ChatBot.LogToConsole("Waiting 5 seconds (" + AttemptsLeft + " attempts left)...");
|
||||
Thread.Sleep(5000); AttemptsLeft--; Program.Restart();
|
||||
ConsoleIO.WriteLogLine("Waiting 5 seconds (" + ReconnectionAttemptsLeft + " attempts left)...");
|
||||
Thread.Sleep(5000); ReconnectionAttemptsLeft--; Program.Restart();
|
||||
}
|
||||
else if (!singlecommand && Settings.interactiveMode)
|
||||
{
|
||||
Program.HandleFailure();
|
||||
}
|
||||
else if (!singlecommand) { Console.ReadLine(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,7 +242,7 @@ namespace MinecraftClient
|
|||
{
|
||||
string response_msg = "";
|
||||
string command = Settings.internalCmdChar == ' ' ? text : text.Substring(1);
|
||||
if (!performInternalCommand(Settings.expandVars(command), ref response_msg) && Settings.internalCmdChar == '/')
|
||||
if (!PerformInternalCommand(Settings.ExpandVars(command), ref response_msg) && Settings.internalCmdChar == '/')
|
||||
{
|
||||
SendText(text);
|
||||
}
|
||||
|
|
@ -204,6 +257,7 @@ namespace MinecraftClient
|
|||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
catch (NullReferenceException) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -214,7 +268,7 @@ namespace MinecraftClient
|
|||
/// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param>
|
||||
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
|
||||
|
||||
public bool performInternalCommand(string command, ref string response_msg)
|
||||
public bool PerformInternalCommand(string command, ref string response_msg)
|
||||
{
|
||||
/* Load commands from the 'Commands' namespace */
|
||||
|
||||
|
|
@ -294,7 +348,69 @@ namespace MinecraftClient
|
|||
|
||||
Thread.Sleep(1000);
|
||||
|
||||
if (client != null) { client.Close(); }
|
||||
if (client != null)
|
||||
client.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a server was successfully joined
|
||||
/// </summary>
|
||||
|
||||
public void OnGameJoined()
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(Settings.BrandInfo))
|
||||
handler.SendBrandInfo(Settings.BrandInfo.Trim());
|
||||
foreach (ChatBot bot in bots)
|
||||
bot.AfterGameJoined();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server sends a new player location,
|
||||
/// or if a ChatBot whishes to update the player's location.
|
||||
/// </summary>
|
||||
/// <param name="location">The new location</param>
|
||||
/// <param name="relative">If true, the location is relative to the current location</param>
|
||||
|
||||
public void UpdateLocation(Location location, bool relative)
|
||||
{
|
||||
lock (locationLock)
|
||||
{
|
||||
if (relative)
|
||||
{
|
||||
this.location += location;
|
||||
}
|
||||
else this.location = location;
|
||||
locationReceived = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server sends a new player location,
|
||||
/// or if a ChatBot whishes to update the player's location.
|
||||
/// </summary>
|
||||
/// <param name="location">The new location</param>
|
||||
/// <param name="relative">If true, the location is relative to the current location</param>
|
||||
|
||||
public void UpdateLocation(Location location)
|
||||
{
|
||||
UpdateLocation(location, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move to the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to reach</param>
|
||||
/// <param name="allowUnsafe">Allow possible but unsafe locations</param>
|
||||
/// <returns>True if a path has been found</returns>
|
||||
public bool MoveTo(Location location, bool allowUnsafe = false)
|
||||
{
|
||||
lock (locationLock)
|
||||
{
|
||||
if (Movement.GetAvailableMoves(world, this.location, allowUnsafe).Contains(location))
|
||||
path = new Queue<Location>(new[] { location });
|
||||
else path = Movement.CalculatePath(world, this.location, location, allowUnsafe);
|
||||
return path != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -305,8 +421,21 @@ namespace MinecraftClient
|
|||
public void OnTextReceived(string text)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted(text, false);
|
||||
foreach (ChatBot bot in new List<ChatBot>(bots))
|
||||
bot.GetText(text);
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -338,7 +467,8 @@ namespace MinecraftClient
|
|||
foreach (ChatBot bot in bots)
|
||||
will_restart |= bot.OnDisconnect(reason, message);
|
||||
|
||||
if (!will_restart) { Program.OfflineCommandPrompt(); }
|
||||
if (!will_restart)
|
||||
Program.HandleFailure();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -347,21 +477,38 @@ namespace MinecraftClient
|
|||
|
||||
public void OnUpdate()
|
||||
{
|
||||
for (int i = 0; i < bots.Count; i++)
|
||||
foreach (var bot in bots.ToArray())
|
||||
{
|
||||
try
|
||||
{
|
||||
bots[i].Update();
|
||||
bot.Update();
|
||||
bot.ProcessQueuedText();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!(e is ThreadAbortException))
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Got error from " + bots[i].ToString() + ": " + e.ToString());
|
||||
ConsoleIO.WriteLineFormatted("§8Update: Got error from " + bot.ToString() + ": " + e.ToString());
|
||||
}
|
||||
else throw; //ThreadAbortException should not be caught
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.TerrainAndMovements && locationReceived)
|
||||
{
|
||||
lock (locationLock)
|
||||
{
|
||||
for (int i = 0; i < 2; i++) //Needs to run at 20 tps; MCC runs at 10 tps
|
||||
{
|
||||
if (steps != null && steps.Count > 0)
|
||||
location = steps.Dequeue();
|
||||
else if (path != null && path.Count > 0)
|
||||
steps = Movement.Move2Steps(location, path.Dequeue());
|
||||
else location = Movement.HandleGravity(world, location);
|
||||
handler.SendLocationUpdate(location, Movement.IsOnGround(world, location));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -387,6 +534,8 @@ namespace MinecraftClient
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
|
@ -412,9 +561,16 @@ namespace MinecraftClient
|
|||
|
||||
public void OnPlayerJoin(Guid uuid, string name)
|
||||
{
|
||||
onlinePlayers[uuid] = name;
|
||||
//Ignore TabListPlus placeholders
|
||||
if (name.StartsWith("0000tab#"))
|
||||
return;
|
||||
|
||||
lock (onlinePlayers)
|
||||
{
|
||||
onlinePlayers[uuid] = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when a player has left the game
|
||||
/// </summary>
|
||||
|
|
@ -422,7 +578,10 @@ namespace MinecraftClient
|
|||
|
||||
public void OnPlayerLeave(Guid uuid)
|
||||
{
|
||||
onlinePlayers.Remove(uuid);
|
||||
lock (onlinePlayers)
|
||||
{
|
||||
onlinePlayers.Remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -430,9 +589,115 @@ namespace MinecraftClient
|
|||
/// </summary>
|
||||
/// <returns>Online player names</returns>
|
||||
|
||||
public string[] getOnlinePlayers()
|
||||
public string[] GetOnlinePlayers()
|
||||
{
|
||||
return onlinePlayers.Values.Distinct().ToArray();
|
||||
lock (onlinePlayers)
|
||||
{
|
||||
return onlinePlayers.Values.Distinct().ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the given plugin channel for the given bot.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to register.</param>
|
||||
/// <param name="bot">The bot to register the channel for.</param>
|
||||
|
||||
public void RegisterPluginChannel(string channel, ChatBot bot)
|
||||
{
|
||||
if (registeredBotPluginChannels.ContainsKey(channel))
|
||||
{
|
||||
registeredBotPluginChannels[channel].Add(bot);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<ChatBot> bots = new List<ChatBot>();
|
||||
bots.Add(bot);
|
||||
registeredBotPluginChannels[channel] = bots;
|
||||
SendPluginChannelMessage("REGISTER", Encoding.UTF8.GetBytes(channel), true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the given plugin channel for the given bot.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to unregister.</param>
|
||||
/// <param name="bot">The bot to unregister the channel for.</param>
|
||||
|
||||
public void UnregisterPluginChannel(string channel, ChatBot bot)
|
||||
{
|
||||
if (registeredBotPluginChannels.ContainsKey(channel))
|
||||
{
|
||||
List<ChatBot> registeredBots = registeredBotPluginChannels[channel];
|
||||
registeredBots.RemoveAll(item => object.ReferenceEquals(item, bot));
|
||||
if (registeredBots.Count == 0)
|
||||
{
|
||||
registeredBotPluginChannels.Remove(channel);
|
||||
SendPluginChannelMessage("UNREGISTER", Encoding.UTF8.GetBytes(channel), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a plugin channel packet to the server. See http://wiki.vg/Plugin_channel for more information
|
||||
/// about plugin channels.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to send the packet on.</param>
|
||||
/// <param name="data">The payload for the packet.</param>
|
||||
/// <param name="sendEvenIfNotRegistered">Whether the packet should be sent even if the server or the client hasn't registered it yet.</param>
|
||||
/// <returns>Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered.</returns>
|
||||
|
||||
public bool SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false)
|
||||
{
|
||||
if (!sendEvenIfNotRegistered)
|
||||
{
|
||||
if (!registeredBotPluginChannels.ContainsKey(channel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!registeredServerPluginChannels.Contains(channel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return handler.SendPluginChannelPacket(channel, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a plugin channel message was sent from the server.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel the message was sent on</param>
|
||||
/// <param name="data">The data from the channel</param>
|
||||
|
||||
public void OnPluginChannelMessage(string channel, byte[] data)
|
||||
{
|
||||
if (channel == "REGISTER")
|
||||
{
|
||||
string[] channels = Encoding.UTF8.GetString(data).Split('\0');
|
||||
foreach (string chan in channels)
|
||||
{
|
||||
if (!registeredServerPluginChannels.Contains(chan))
|
||||
{
|
||||
registeredServerPluginChannels.Add(chan);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (channel == "UNREGISTER")
|
||||
{
|
||||
string[] channels = Encoding.UTF8.GetString(data).Split('\0');
|
||||
foreach (string chan in channels)
|
||||
{
|
||||
registeredServerPluginChannels.Remove(chan);
|
||||
}
|
||||
}
|
||||
|
||||
if (registeredBotPluginChannels.ContainsKey(channel))
|
||||
{
|
||||
foreach (ChatBot bot in registeredBotPluginChannels[channel])
|
||||
{
|
||||
bot.OnPluginMessage(channel, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@
|
|||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
|
@ -74,6 +75,7 @@
|
|||
<Compile Include="AutoTimeout.cs" />
|
||||
<Compile Include="ChatBots\Alerts.cs" />
|
||||
<Compile Include="ChatBots\AntiAFK.cs" />
|
||||
<Compile Include="ChatBots\AutoRespond.cs" />
|
||||
<Compile Include="ChatBots\AutoRelog.cs" />
|
||||
<Compile Include="ChatBots\ChatLog.cs" />
|
||||
<Compile Include="ChatBots\HangmanGame.cs" />
|
||||
|
|
@ -85,6 +87,7 @@
|
|||
<Compile Include="ChatBot.cs" />
|
||||
<Compile Include="Command.cs" />
|
||||
<Compile Include="Commands\Connect.cs" />
|
||||
<Compile Include="Commands\Move.cs" />
|
||||
<Compile Include="Commands\Exit.cs" />
|
||||
<Compile Include="Commands\Log.cs" />
|
||||
<Compile Include="Commands\Reco.cs" />
|
||||
|
|
@ -94,9 +97,35 @@
|
|||
<Compile Include="Commands\Set.cs" />
|
||||
<Compile Include="ConsoleIcon.cs" />
|
||||
<Compile Include="ConsoleIO.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\AesFastEngine.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\BufferedBlockCipher.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\BufferedCipherBase.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\CfbBlockCipher.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\Check.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\CipherStream.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\CryptoException.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\DataLengthException.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\IBlockCipher.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\IBufferedCipher.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\ICipherParameters.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\KeyParameter.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\OutputLengthException.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\Pack.cs" />
|
||||
<Compile Include="Crypto\Streams\BouncyAes\ParametersWithIV.cs" />
|
||||
<Compile Include="Crypto\Streams\MonoAesStream.cs" />
|
||||
<Compile Include="Crypto\Streams\RegularAesStream.cs" />
|
||||
<Compile Include="Crypto\CryptoHandler.cs" />
|
||||
<Compile Include="CSharpRunner.cs" />
|
||||
<Compile Include="Mapping\Block.cs" />
|
||||
<Compile Include="Mapping\Chunk.cs" />
|
||||
<Compile Include="Mapping\ChunkColumn.cs" />
|
||||
<Compile Include="Mapping\Direction.cs" />
|
||||
<Compile Include="Mapping\Material.cs" />
|
||||
<Compile Include="Mapping\Movement.cs" />
|
||||
<Compile Include="Mapping\World.cs" />
|
||||
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeClientState.cs" />
|
||||
<Compile Include="Protocol\Handlers\Forge\FMLHandshakeDiscriminator.cs" />
|
||||
<Compile Include="Protocol\Handlers\Forge\ForgeInfo.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\CRC32.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\Deflate.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\GZipStream.cs" />
|
||||
|
|
@ -107,10 +136,10 @@
|
|||
<Compile Include="Protocol\Handlers\Compression\ZlibBaseStream.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\ZlibCodec.cs" />
|
||||
<Compile Include="Protocol\Handlers\Compression\ZlibConstants.cs" />
|
||||
<Compile Include="Protocol\Handlers\Json.cs" />
|
||||
<Compile Include="Protocol\Handlers\ZlibUtils.cs" />
|
||||
<Compile Include="Protocol\Handlers\ChatParser.cs" />
|
||||
<Compile Include="Crypto\IAesStream.cs" />
|
||||
<Compile Include="Crypto\IPaddingProvider.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="McTcpClient.cs" />
|
||||
|
|
@ -119,8 +148,10 @@
|
|||
<Compile Include="Protocol\Handlers\Protocol16.cs" />
|
||||
<Compile Include="Protocol\IMinecraftCom.cs" />
|
||||
<Compile Include="Protocol\IMinecraftComHandler.cs" />
|
||||
<Compile Include="Protocol\Handlers\Protocol17.cs" />
|
||||
<Compile Include="Protocol\ProtocolHandler.cs" />
|
||||
<Compile Include="Protocol\SessionCache\CacheType.cs" />
|
||||
<Compile Include="Protocol\SessionCache\SessionCache.cs" />
|
||||
<Compile Include="Protocol\SessionToken.cs" />
|
||||
<Compile Include="Proxy\ProxyHandler.cs" />
|
||||
<Compile Include="Proxy\Handlers\EventArgs\CreateConnectionAsyncCompletedEventArgs.cs" />
|
||||
<Compile Include="Proxy\Handlers\Exceptions\ProxyException.cs" />
|
||||
|
|
@ -133,6 +164,7 @@
|
|||
<Compile Include="Proxy\Handlers\Utils.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Commands\List.cs" />
|
||||
<Compile Include="Mapping\Location.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
|
||||
|
|
|
|||
|
|
@ -1,329 +1,412 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Minecraft Console Client by ORelio (c) 2012-2014.
|
||||
/// 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>
|
||||
|
||||
static class Program
|
||||
{
|
||||
private static McTcpClient Client;
|
||||
public static string[] startupargs;
|
||||
public const string Version = "1.8.0";
|
||||
private static Thread offlinePrompt = null;
|
||||
|
||||
/// <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.8.1 - v" + Version + " - By ORelio & Contributors");
|
||||
|
||||
//Basic Input/Output ?
|
||||
if (args.Length >= 1 && args[args.Length - 1] == "BasicIO")
|
||||
{
|
||||
ConsoleIO.basicIO = true;
|
||||
Console.OutputEncoding = Console.InputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage);
|
||||
args = args.Where(o => !Object.ReferenceEquals(o, args[args.Length - 1])).ToArray();
|
||||
}
|
||||
|
||||
//Process ini configuration file
|
||||
if (args.Length >= 1 && System.IO.File.Exists(args[0]) && System.IO.Path.GetExtension(args[0]).ToLower() == ".ini")
|
||||
{
|
||||
Settings.LoadSettings(args[0]);
|
||||
|
||||
//remove ini configuration file from arguments array
|
||||
List<string> args_tmp = args.ToList<string>();
|
||||
args_tmp.RemoveAt(0);
|
||||
args = args_tmp.ToArray();
|
||||
}
|
||||
else if (System.IO.File.Exists("MinecraftClient.ini"))
|
||||
{
|
||||
Settings.LoadSettings("MinecraftClient.ini");
|
||||
}
|
||||
else Settings.WriteDefaultSettings("MinecraftClient.ini");
|
||||
|
||||
//Other command-line arguments
|
||||
if (args.Length >= 1)
|
||||
{
|
||||
Settings.Login = args[0];
|
||||
if (args.Length >= 2)
|
||||
{
|
||||
Settings.Password = args[1];
|
||||
if (args.Length >= 3)
|
||||
{
|
||||
Settings.setServerIP(args[2]);
|
||||
|
||||
//Single command?
|
||||
if (args.Length >= 4)
|
||||
{
|
||||
Settings.SingleCommand = args[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.ConsoleTitle != "")
|
||||
{
|
||||
Settings.Username = "New Window";
|
||||
Console.Title = Settings.expandVars(Settings.ConsoleTitle);
|
||||
}
|
||||
|
||||
//Asking the user to type in missing data such as Username and Password
|
||||
|
||||
if (Settings.Login == "")
|
||||
{
|
||||
Console.Write(ConsoleIO.basicIO ? "Please type the username of your choice.\n" : "Username : ");
|
||||
Settings.Login = Console.ReadLine();
|
||||
}
|
||||
if (Settings.Password == "")
|
||||
{
|
||||
Console.Write(ConsoleIO.basicIO ? "Please type the password for " + Settings.Login + ".\n" : "Password : ");
|
||||
Settings.Password = ConsoleIO.basicIO ? Console.ReadLine() : ConsoleIO.ReadPassword();
|
||||
if (Settings.Password == "") { Settings.Password = "-"; }
|
||||
if (!ConsoleIO.basicIO)
|
||||
{
|
||||
//Hide password length
|
||||
Console.CursorTop--; Console.Write("Password : <******>");
|
||||
for (int i = 19; i < Console.BufferWidth; i++) { Console.Write(' '); }
|
||||
}
|
||||
}
|
||||
|
||||
startupargs = args;
|
||||
InitializeClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a new Client
|
||||
/// </summary>
|
||||
|
||||
private static void InitializeClient()
|
||||
{
|
||||
ProtocolHandler.LoginResult result;
|
||||
Settings.Username = Settings.Login;
|
||||
string sessionID = "";
|
||||
string UUID = "";
|
||||
|
||||
if (Settings.Password == "-")
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8You chose to run in offline mode.");
|
||||
result = ProtocolHandler.LoginResult.Success;
|
||||
sessionID = "0";
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Connecting to Minecraft.net...");
|
||||
result = ProtocolHandler.GetLogin(ref Settings.Username, Settings.Password, ref sessionID, ref UUID);
|
||||
}
|
||||
|
||||
if (result == ProtocolHandler.LoginResult.Success)
|
||||
{
|
||||
if (Settings.ConsoleTitle != "")
|
||||
Console.Title = Settings.expandVars(Settings.ConsoleTitle);
|
||||
|
||||
if (Settings.playerHeadAsIcon)
|
||||
ConsoleIcon.setPlayerIconAsync(Settings.Username);
|
||||
|
||||
Console.WriteLine("Success. (session ID: " + sessionID + ')');
|
||||
|
||||
if (Settings.ServerIP == "")
|
||||
{
|
||||
Console.Write("Server IP : ");
|
||||
Settings.setServerIP(Console.ReadLine());
|
||||
}
|
||||
|
||||
//Get server version
|
||||
int protocolversion = 0;
|
||||
|
||||
if (Settings.ServerVersion != "" && Settings.ServerVersion.ToLower() != "auto")
|
||||
{
|
||||
protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion);
|
||||
if (protocolversion != 0)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Using Minecraft version " + Settings.ServerVersion + " (protocol v" + protocolversion + ')');
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Unknown or not supported MC version '" + Settings.ServerVersion + "'.\nSwitching to autodetection mode.");
|
||||
}
|
||||
|
||||
if (protocolversion == 0)
|
||||
{
|
||||
Console.WriteLine("Retrieving Server Info...");
|
||||
if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion))
|
||||
{
|
||||
Console.WriteLine("Failed to ping this IP.");
|
||||
if (Settings.AutoRelog_Enabled)
|
||||
{
|
||||
ChatBots.AutoRelog bot = new ChatBots.AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries);
|
||||
if (!bot.OnDisconnect(ChatBot.DisconnectReason.ConnectionLost, "Failed to ping this IP.")) { OfflineCommandPrompt(); }
|
||||
}
|
||||
else OfflineCommandPrompt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolversion != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Start the main TCP client
|
||||
if (Settings.SingleCommand != "")
|
||||
{
|
||||
Client = new McTcpClient(Settings.Username, UUID, sessionID, Settings.ServerIP, Settings.ServerPort, protocolversion, Settings.SingleCommand);
|
||||
}
|
||||
else Client = new McTcpClient(Settings.Username, UUID, sessionID, protocolversion, Settings.ServerIP, Settings.ServerPort);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
Console.WriteLine("Cannot connect to the server : This version is not supported !");
|
||||
OfflineCommandPrompt();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Failed to determine server version.");
|
||||
OfflineCommandPrompt();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.Write("Connection failed : ");
|
||||
switch (result)
|
||||
{
|
||||
case ProtocolHandler.LoginResult.AccountMigrated: Console.WriteLine("Account migrated, use e-mail as username."); break;
|
||||
case ProtocolHandler.LoginResult.ServiceUnavailable: Console.WriteLine("Login servers are unavailable. Please try again later."); break;
|
||||
case ProtocolHandler.LoginResult.WrongPassword: Console.WriteLine("Incorrect password."); break;
|
||||
case ProtocolHandler.LoginResult.NotPremium: Console.WriteLine("User not premium."); break;
|
||||
case ProtocolHandler.LoginResult.OtherError: Console.WriteLine("Network error."); break;
|
||||
case ProtocolHandler.LoginResult.SSLError: Console.WriteLine("SSL Error.");
|
||||
if (isUsingMono)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8It appears that you are using Mono to run this program."
|
||||
+ '\n' + "The first time, you have to import HTTPS certificates using:"
|
||||
+ '\n' + "mozroots --import --ask-remove");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
while (Console.KeyAvailable) { Console.ReadKey(false); }
|
||||
if (Settings.SingleCommand == "") { OfflineCommandPrompt(); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect the current client from the server and restart it
|
||||
/// </summary>
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
new Thread(new ThreadStart(delegate
|
||||
{
|
||||
if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); }
|
||||
if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); }
|
||||
Console.WriteLine("Restarting Minecraft Console Client...");
|
||||
InitializeClient();
|
||||
})).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect the current client from the server and exit the app
|
||||
/// </summary>
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
new Thread(new ThreadStart(delegate
|
||||
{
|
||||
if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); }
|
||||
if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); }
|
||||
if (Settings.playerHeadAsIcon) { ConsoleIcon.revertToCMDIcon(); }
|
||||
Environment.Exit(0);
|
||||
})).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause the program, usually when an error or a kick occured, letting the user press Enter to quit OR type /reconnect
|
||||
/// </summary>
|
||||
|
||||
public static void OfflineCommandPrompt()
|
||||
{
|
||||
if (!Settings.exitOnFailure && offlinePrompt == null)
|
||||
{
|
||||
offlinePrompt = new Thread(new ThreadStart(delegate
|
||||
{
|
||||
string command = " ";
|
||||
ConsoleIO.WriteLineFormatted("Not connected to any server. Use '" + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + "help' for help.");
|
||||
ConsoleIO.WriteLineFormatted("Or press Enter to exit Minecraft Console Client.");
|
||||
while (command.Length > 0)
|
||||
{
|
||||
if (!ConsoleIO.basicIO) { ConsoleIO.Write('>'); }
|
||||
command = Console.ReadLine().Trim();
|
||||
if (command.Length > 0)
|
||||
{
|
||||
if (Settings.internalCmdChar != ' ' && command[0] == Settings.internalCmdChar)
|
||||
{
|
||||
string message = "";
|
||||
command = command.Substring(1);
|
||||
if (command.StartsWith("reco"))
|
||||
{
|
||||
message = new Commands.Reco().Run(null, Settings.expandVars(command));
|
||||
}
|
||||
else if (command.StartsWith("connect"))
|
||||
{
|
||||
message = new Commands.Connect().Run(null, Settings.expandVars(command));
|
||||
}
|
||||
else if (command.StartsWith("exit") || command.StartsWith("quit"))
|
||||
{
|
||||
message = new Commands.Exit().Run(null, Settings.expandVars(command));
|
||||
}
|
||||
else if (command.StartsWith("help"))
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Reco().CMDDesc);
|
||||
ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Connect().CMDDesc);
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Unknown command '" + command.Split(' ')[0] + "'.");
|
||||
if (message != "") { ConsoleIO.WriteLineFormatted("§8MCC: " + message); }
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Please type a command or press Enter to exit.");
|
||||
}
|
||||
}
|
||||
}));
|
||||
offlinePrompt.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect if the user is running Minecraft Console Client through Mono
|
||||
/// </summary>
|
||||
|
||||
public static bool isUsingMono
|
||||
{
|
||||
get
|
||||
{
|
||||
return Type.GetType("Mono.Runtime") != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate types in namespace through reflection
|
||||
/// </summary>
|
||||
/// <param name="nameSpace">Namespace to process</param>
|
||||
/// <param name="assembly">Assembly to use. Default is Assembly.GetExecutingAssembly()</param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static Type[] GetTypesInNamespace(string nameSpace, Assembly assembly = null)
|
||||
{
|
||||
if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); }
|
||||
return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
using MinecraftClient.Protocol.SessionCache;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Minecraft Console Client by ORelio and Contributors (c) 2012-2016.
|
||||
/// 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>
|
||||
|
||||
static class Program
|
||||
{
|
||||
private static McTcpClient Client;
|
||||
public static string[] startupargs;
|
||||
|
||||
public const string Version = "1.9.0 BETA";
|
||||
public const string MCLowestVersion = "1.4.6";
|
||||
public const string MCHighestVersion = "1.9.0";
|
||||
|
||||
private static Thread offlinePrompt = null;
|
||||
private static bool useMcVersionOnce = false;
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point of Minecraft Console Client
|
||||
/// </summary>
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Console Client for MC {0} to {1} - v{2} - By ORelio & Contributors", MCLowestVersion, MCHighestVersion, Version);
|
||||
|
||||
//Basic Input/Output ?
|
||||
if (args.Length >= 1 && args[args.Length - 1] == "BasicIO")
|
||||
{
|
||||
ConsoleIO.basicIO = true;
|
||||
Console.OutputEncoding = Console.InputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage);
|
||||
args = args.Where(o => !Object.ReferenceEquals(o, args[args.Length - 1])).ToArray();
|
||||
}
|
||||
|
||||
//Process ini configuration file
|
||||
if (args.Length >= 1 && System.IO.File.Exists(args[0]) && System.IO.Path.GetExtension(args[0]).ToLower() == ".ini")
|
||||
{
|
||||
Settings.LoadSettings(args[0]);
|
||||
|
||||
//remove ini configuration file from arguments array
|
||||
List<string> args_tmp = args.ToList<string>();
|
||||
args_tmp.RemoveAt(0);
|
||||
args = args_tmp.ToArray();
|
||||
}
|
||||
else if (System.IO.File.Exists("MinecraftClient.ini"))
|
||||
{
|
||||
Settings.LoadSettings("MinecraftClient.ini");
|
||||
}
|
||||
else Settings.WriteDefaultSettings("MinecraftClient.ini");
|
||||
|
||||
//Other command-line arguments
|
||||
if (args.Length >= 1)
|
||||
{
|
||||
Settings.Login = args[0];
|
||||
if (args.Length >= 2)
|
||||
{
|
||||
Settings.Password = args[1];
|
||||
if (args.Length >= 3)
|
||||
{
|
||||
Settings.SetServerIP(args[2]);
|
||||
|
||||
//Single command?
|
||||
if (args.Length >= 4)
|
||||
{
|
||||
Settings.SingleCommand = args[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.ConsoleTitle != "")
|
||||
{
|
||||
Settings.Username = "New Window";
|
||||
Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
|
||||
}
|
||||
|
||||
//Load cached sessions from disk if necessary
|
||||
if (Settings.SessionCaching == CacheType.Disk)
|
||||
{
|
||||
bool cacheLoaded = SessionCache.InitializeDiskCache();
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted(cacheLoaded ? "§8Session cache has been successfully loaded from disk." : "§8Cached sessions could not be loaded from disk");
|
||||
}
|
||||
|
||||
//Asking the user to type in missing data such as Username and Password
|
||||
|
||||
if (Settings.Login == "")
|
||||
{
|
||||
Console.Write(ConsoleIO.basicIO ? "Please type the username or email of your choice.\n" : "Login : ");
|
||||
Settings.Login = Console.ReadLine();
|
||||
}
|
||||
if (Settings.Password == "" && (Settings.SessionCaching == CacheType.None || !SessionCache.Contains(Settings.Login.ToLower())))
|
||||
{
|
||||
RequestPassword();
|
||||
}
|
||||
|
||||
startupargs = args;
|
||||
InitializeClient();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduest user to submit password.
|
||||
/// </summary>
|
||||
private static void RequestPassword()
|
||||
{
|
||||
Console.Write(ConsoleIO.basicIO ? "Please type the password for " + Settings.Login + ".\n" : "Password : ");
|
||||
Settings.Password = ConsoleIO.basicIO ? Console.ReadLine() : ConsoleIO.ReadPassword();
|
||||
if (Settings.Password == "") { Settings.Password = "-"; }
|
||||
if (!ConsoleIO.basicIO)
|
||||
{
|
||||
//Hide password length
|
||||
Console.CursorTop--; Console.Write("Password : <******>");
|
||||
for (int i = 19; i < Console.BufferWidth; i++) { Console.Write(' '); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start a new Client
|
||||
/// </summary>
|
||||
|
||||
private static void InitializeClient()
|
||||
{
|
||||
SessionToken session = new SessionToken();
|
||||
|
||||
ProtocolHandler.LoginResult result = ProtocolHandler.LoginResult.LoginRequired;
|
||||
|
||||
if (Settings.Password == "-")
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8You chose to run in offline mode.");
|
||||
result = ProtocolHandler.LoginResult.Success;
|
||||
session.PlayerID = "0";
|
||||
session.PlayerName = Settings.Login;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Validate cached session or login new session.
|
||||
if (Settings.SessionCaching != CacheType.None && SessionCache.Contains(Settings.Login.ToLower()))
|
||||
{
|
||||
session = SessionCache.Get(Settings.Login.ToLower());
|
||||
result = ProtocolHandler.GetTokenValidation(session);
|
||||
if (result != ProtocolHandler.LoginResult.Success)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Cached session is invalid or expired.");
|
||||
if (Settings.Password == "")
|
||||
RequestPassword();
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Cached session is still valid for " + session.PlayerName + '.');
|
||||
}
|
||||
|
||||
if (result != ProtocolHandler.LoginResult.Success)
|
||||
{
|
||||
Console.WriteLine("Connecting to Minecraft.net...");
|
||||
result = ProtocolHandler.GetLogin(Settings.Login, Settings.Password, out session);
|
||||
|
||||
if (result == ProtocolHandler.LoginResult.Success && Settings.SessionCaching != CacheType.None)
|
||||
{
|
||||
SessionCache.Store(Settings.Login.ToLower(), session);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (result == ProtocolHandler.LoginResult.Success)
|
||||
{
|
||||
Settings.Username = session.PlayerName;
|
||||
|
||||
if (Settings.ConsoleTitle != "")
|
||||
Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
|
||||
|
||||
if (Settings.playerHeadAsIcon)
|
||||
ConsoleIcon.setPlayerIconAsync(Settings.Username);
|
||||
|
||||
if (Settings.DebugMessages)
|
||||
Console.WriteLine("Success. (session ID: " + session.ID + ')');
|
||||
|
||||
//ProtocolHandler.RealmsListWorlds(Settings.Username, PlayerID, sessionID); //TODO REMOVE
|
||||
|
||||
if (Settings.ServerIP == "")
|
||||
{
|
||||
Console.Write("Server IP : ");
|
||||
Settings.SetServerIP(Console.ReadLine());
|
||||
}
|
||||
|
||||
//Get server version
|
||||
int protocolversion = 0;
|
||||
ForgeInfo forgeInfo = null;
|
||||
|
||||
if (Settings.ServerVersion != "" && Settings.ServerVersion.ToLower() != "auto")
|
||||
{
|
||||
protocolversion = Protocol.ProtocolHandler.MCVer2ProtocolVersion(Settings.ServerVersion);
|
||||
|
||||
if (protocolversion != 0)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Using Minecraft version " + Settings.ServerVersion + " (protocol v" + protocolversion + ')');
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Unknown or not supported MC version '" + Settings.ServerVersion + "'.\nSwitching to autodetection mode.");
|
||||
|
||||
if (useMcVersionOnce)
|
||||
{
|
||||
useMcVersionOnce = false;
|
||||
Settings.ServerVersion = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolversion == 0)
|
||||
{
|
||||
Console.WriteLine("Retrieving Server Info...");
|
||||
if (!ProtocolHandler.GetServerInfo(Settings.ServerIP, Settings.ServerPort, ref protocolversion, ref forgeInfo))
|
||||
{
|
||||
HandleFailure("Failed to ping this IP.", true, ChatBots.AutoRelog.DisconnectReason.ConnectionLost);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolversion != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Start the main TCP client
|
||||
if (Settings.SingleCommand != "")
|
||||
{
|
||||
Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, Settings.ServerIP, Settings.ServerPort, protocolversion, forgeInfo, Settings.SingleCommand);
|
||||
}
|
||||
else Client = new McTcpClient(session.PlayerName, session.PlayerID, session.ID, protocolversion, forgeInfo, Settings.ServerIP, Settings.ServerPort);
|
||||
|
||||
//Update console title
|
||||
if (Settings.ConsoleTitle != "")
|
||||
Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
|
||||
}
|
||||
catch (NotSupportedException) { HandleFailure("Cannot connect to the server : This version is not supported !", true); }
|
||||
}
|
||||
else HandleFailure("Failed to determine server version.", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
string failureMessage = "Minecraft Login failed : ";
|
||||
switch (result)
|
||||
{
|
||||
case ProtocolHandler.LoginResult.AccountMigrated: failureMessage += "Account migrated, use e-mail as username."; break;
|
||||
case ProtocolHandler.LoginResult.ServiceUnavailable: failureMessage += "Login servers are unavailable. Please try again later."; break;
|
||||
case ProtocolHandler.LoginResult.WrongPassword: failureMessage += "Incorrect password."; break;
|
||||
case ProtocolHandler.LoginResult.NotPremium: failureMessage += "User not premium."; break;
|
||||
case ProtocolHandler.LoginResult.OtherError: failureMessage += "Network error."; break;
|
||||
case ProtocolHandler.LoginResult.SSLError: failureMessage += "SSL Error."; break;
|
||||
default: failureMessage += "Unknown Error."; break;
|
||||
}
|
||||
if (result == ProtocolHandler.LoginResult.SSLError && isUsingMono)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8It appears that you are using Mono to run this program."
|
||||
+ '\n' + "The first time, you have to import HTTPS certificates using:"
|
||||
+ '\n' + "mozroots --import --ask-remove");
|
||||
return;
|
||||
}
|
||||
HandleFailure(failureMessage, false, ChatBot.DisconnectReason.LoginRejected);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect the current client from the server and restart it
|
||||
/// </summary>
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
new Thread(new ThreadStart(delegate
|
||||
{
|
||||
if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); }
|
||||
if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); }
|
||||
Console.WriteLine("Restarting Minecraft Console Client...");
|
||||
InitializeClient();
|
||||
})).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect the current client from the server and exit the app
|
||||
/// </summary>
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
new Thread(new ThreadStart(delegate
|
||||
{
|
||||
if (Client != null) { Client.Disconnect(); ConsoleIO.Reset(); }
|
||||
if (offlinePrompt != null) { offlinePrompt.Abort(); offlinePrompt = null; ConsoleIO.Reset(); }
|
||||
if (Settings.playerHeadAsIcon) { ConsoleIcon.revertToCMDIcon(); }
|
||||
Environment.Exit(0);
|
||||
})).Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle fatal errors such as ping failure, login failure, server disconnection, and so on.
|
||||
/// Allows AutoRelog to perform on fatal errors, prompt for server version, and offline commands.
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">Error message to display and optionally pass to AutoRelog bot</param>
|
||||
/// <param name="versionError">Specify if the error is related to an incompatible or unkown server version</param>
|
||||
/// <param name="disconnectReason">If set, the error message will be processed by the AutoRelog bot</param>
|
||||
|
||||
public static void HandleFailure(string errorMessage = null, bool versionError = false, ChatBots.AutoRelog.DisconnectReason? disconnectReason = null)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
ConsoleIO.Reset();
|
||||
while (Console.KeyAvailable)
|
||||
Console.ReadKey(true);
|
||||
Console.WriteLine(errorMessage);
|
||||
|
||||
if (disconnectReason.HasValue)
|
||||
{
|
||||
if (ChatBots.AutoRelog.OnDisconnectStatic(disconnectReason.Value, errorMessage))
|
||||
return; //AutoRelog is triggering a restart of the client
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.interactiveMode)
|
||||
{
|
||||
if (versionError)
|
||||
{
|
||||
Console.Write("Server version : ");
|
||||
Settings.ServerVersion = Console.ReadLine();
|
||||
if (Settings.ServerVersion != "")
|
||||
{
|
||||
useMcVersionOnce = true;
|
||||
Restart();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (offlinePrompt == null)
|
||||
{
|
||||
offlinePrompt = new Thread(new ThreadStart(delegate
|
||||
{
|
||||
string command = " ";
|
||||
ConsoleIO.WriteLineFormatted("Not connected to any server. Use '" + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + "help' for help.");
|
||||
ConsoleIO.WriteLineFormatted("Or press Enter to exit Minecraft Console Client.");
|
||||
while (command.Length > 0)
|
||||
{
|
||||
if (!ConsoleIO.basicIO) { ConsoleIO.Write('>'); }
|
||||
command = Console.ReadLine().Trim();
|
||||
if (command.Length > 0)
|
||||
{
|
||||
string message = "";
|
||||
|
||||
if (Settings.internalCmdChar != ' '
|
||||
&& command[0] == Settings.internalCmdChar)
|
||||
command = command.Substring(1);
|
||||
|
||||
if (command.StartsWith("reco"))
|
||||
{
|
||||
message = new Commands.Reco().Run(null, Settings.ExpandVars(command));
|
||||
}
|
||||
else if (command.StartsWith("connect"))
|
||||
{
|
||||
message = new Commands.Connect().Run(null, Settings.ExpandVars(command));
|
||||
}
|
||||
else if (command.StartsWith("exit") || command.StartsWith("quit"))
|
||||
{
|
||||
message = new Commands.Exit().Run(null, Settings.ExpandVars(command));
|
||||
}
|
||||
else if (command.StartsWith("help"))
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Reco().CMDDesc);
|
||||
ConsoleIO.WriteLineFormatted("§8MCC: " + (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar) + new Commands.Connect().CMDDesc);
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Unknown command '" + command.Split(' ')[0] + "'.");
|
||||
|
||||
if (message != "")
|
||||
ConsoleIO.WriteLineFormatted("§8MCC: " + message);
|
||||
}
|
||||
}
|
||||
}));
|
||||
offlinePrompt.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect if the user is running Minecraft Console Client through Mono
|
||||
/// </summary>
|
||||
|
||||
public static bool isUsingMono
|
||||
{
|
||||
get
|
||||
{
|
||||
return Type.GetType("Mono.Runtime") != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate types in namespace through reflection
|
||||
/// </summary>
|
||||
/// <param name="nameSpace">Namespace to process</param>
|
||||
/// <param name="assembly">Assembly to use. Default is Assembly.GetExecutingAssembly()</param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static Type[] GetTypesInNamespace(string nameSpace, Assembly assembly = null)
|
||||
{
|
||||
if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); }
|
||||
return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,31 +19,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
public static string ParseText(string json)
|
||||
{
|
||||
int cursorpos = 0;
|
||||
JSONData jsonData = String2Data(json, ref cursorpos);
|
||||
return JSONData2String(jsonData, "");
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
return JSONData2String(Json.ParseJson(json), "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -109,12 +85,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
ConsoleIO.WriteLine("Downloading '" + Settings.Language + ".lang' from Mojang servers...");
|
||||
try
|
||||
{
|
||||
string assets_index = downloadString(Settings.TranslationsFile_Website_Index);
|
||||
string[] tmp = assets_index.Split(new string[] { "lang/" + Settings.Language + ".lang" }, StringSplitOptions.None);
|
||||
tmp = tmp[1].Split(new string[] { "hash\": \"" }, StringSplitOptions.None);
|
||||
string hash = tmp[1].Split('"')[0]; //Translations file identifier on Mojang's servers
|
||||
System.IO.File.WriteAllText(Language_File, downloadString(Settings.TranslationsFile_Website_Download + '/' + hash.Substring(0, 2) + '/' + hash));
|
||||
ConsoleIO.WriteLine("Done. File saved as '" + Language_File + '\'');
|
||||
string assets_index = downloadString(Settings.TranslationsFile_Website_Index);
|
||||
string[] tmp = assets_index.Split(new string[] { "minecraft/lang/" + Settings.Language + ".lang" }, StringSplitOptions.None);
|
||||
tmp = tmp[1].Split(new string[] { "hash\": \"" }, StringSplitOptions.None);
|
||||
string hash = tmp[1].Split('"')[0]; //Translations file identifier on Mojang's servers
|
||||
System.IO.File.WriteAllText(Language_File, downloadString(Settings.TranslationsFile_Website_Download + '/' + hash.Substring(0, 2) + '/' + hash));
|
||||
ConsoleIO.WriteLine("Done. File saved as '" + Language_File + '\'');
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -147,7 +123,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
ConsoleIO.WriteLineFormatted("§8Translations file loaded.");
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Translations file loaded.");
|
||||
}
|
||||
else //No external dictionnary found.
|
||||
{
|
||||
|
|
@ -169,158 +146,68 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (!init) { InitRules(); init = true; }
|
||||
if (TranslationRules.ContainsKey(rulename))
|
||||
{
|
||||
if ((TranslationRules[rulename].IndexOf("%1$s") >= 0 && TranslationRules[rulename].IndexOf("%2$s") >= 0)
|
||||
&& (TranslationRules[rulename].IndexOf("%1$s") > TranslationRules[rulename].IndexOf("%2$s")))
|
||||
int using_idx = 0;
|
||||
string rule = TranslationRules[rulename];
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < rule.Length; i++)
|
||||
{
|
||||
while (using_data.Count < 2) { using_data.Add(""); }
|
||||
string tmp = using_data[0];
|
||||
using_data[0] = using_data[1];
|
||||
using_data[1] = tmp;
|
||||
if (rule[i] == '%' && i + 1 < rule.Length)
|
||||
{
|
||||
//Using string or int with %s or %d
|
||||
if (rule[i + 1] == 's' || rule[i + 1] == 'd')
|
||||
{
|
||||
if (using_data.Count > using_idx)
|
||||
{
|
||||
result.Append(using_data[using_idx]);
|
||||
using_idx++;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Using specified string or int with %1$s, %2$s...
|
||||
else if (char.IsDigit(rule[i + 1])
|
||||
&& i + 3 < rule.Length && rule[i + 2] == '$'
|
||||
&& (rule[i + 3] == 's' || rule[i + 3] == 'd'))
|
||||
{
|
||||
int specified_idx = rule[i + 1] - '1';
|
||||
if (using_data.Count > specified_idx)
|
||||
{
|
||||
result.Append(using_data[specified_idx]);
|
||||
using_idx++;
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.Append(rule[i]);
|
||||
}
|
||||
string[] syntax = TranslationRules[rulename].Split(new string[] { "%s", "%d", "%1$s", "%2$s" }, 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;
|
||||
return result.ToString();
|
||||
}
|
||||
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] == '\\')
|
||||
{
|
||||
try //Unicode character \u0123
|
||||
{
|
||||
if (toparse[cursorpos + 1] == 'u'
|
||||
&& isHex(toparse[cursorpos + 2])
|
||||
&& isHex(toparse[cursorpos + 3])
|
||||
&& isHex(toparse[cursorpos + 4])
|
||||
&& isHex(toparse[cursorpos + 5]))
|
||||
{
|
||||
//"abc\u0123abc" => "0123" => 0123 => Unicode char n°0123 => Add char to string
|
||||
data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4), System.Globalization.NumberStyles.HexNumber));
|
||||
cursorpos += 6; continue;
|
||||
}
|
||||
else cursorpos++; //Normal character escapement \"
|
||||
}
|
||||
catch (IndexOutOfRangeException) { cursorpos++; } // \u01<end of string>
|
||||
catch (ArgumentOutOfRangeException) { cursorpos++; } // Unicode index 0123 was invalid
|
||||
}
|
||||
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>
|
||||
/// <param name="colorcode">Allow parent color code to affect child elements (set to "" for function init)</param>
|
||||
/// <returns>returns the Minecraft-formatted string</returns>
|
||||
|
||||
private static string JSONData2String(JSONData data, string colorcode)
|
||||
|
||||
private static string JSONData2String(Json.JSONData data, string colorcode)
|
||||
{
|
||||
string extra_result = "";
|
||||
switch (data.Type)
|
||||
{
|
||||
case JSONData.DataType.Object:
|
||||
case Json.JSONData.DataType.Object:
|
||||
if (data.Properties.ContainsKey("color"))
|
||||
{
|
||||
colorcode = color2tag(JSONData2String(data.Properties["color"], ""));
|
||||
}
|
||||
if (data.Properties.ContainsKey("extra"))
|
||||
{
|
||||
JSONData[] extras = data.Properties["extra"].DataArray.ToArray();
|
||||
foreach (JSONData item in extras)
|
||||
Json.JSONData[] extras = data.Properties["extra"].DataArray.ToArray();
|
||||
foreach (Json.JSONData item in extras)
|
||||
extra_result = extra_result + JSONData2String(item, colorcode) + "§r";
|
||||
}
|
||||
if (data.Properties.ContainsKey("text"))
|
||||
|
|
@ -334,7 +221,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
data.Properties["with"] = data.Properties["using"];
|
||||
if (data.Properties.ContainsKey("with"))
|
||||
{
|
||||
JSONData[] array = data.Properties["with"].DataArray.ToArray();
|
||||
Json.JSONData[] array = data.Properties["with"].DataArray.ToArray();
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
using_data.Add(JSONData2String(array[i], colorcode));
|
||||
|
|
@ -344,29 +231,21 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
else return extra_result;
|
||||
|
||||
case JSONData.DataType.Array:
|
||||
case Json.JSONData.DataType.Array:
|
||||
string result = "";
|
||||
foreach (JSONData item in data.DataArray)
|
||||
foreach (Json.JSONData item in data.DataArray)
|
||||
{
|
||||
result += JSONData2String(item, colorcode);
|
||||
}
|
||||
return result;
|
||||
|
||||
case JSONData.DataType.String:
|
||||
case Json.JSONData.DataType.String:
|
||||
return colorcode + data.StringValue;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Small function for checking if a char is an hexadecimal char (0-9 A-F a-f)
|
||||
/// </summary>
|
||||
/// <param name="c">Char to test</param>
|
||||
/// <returns>True if hexadecimal</returns>
|
||||
|
||||
private static bool isHex(char c) { return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')); }
|
||||
|
||||
/// <summary>
|
||||
/// Do a HTTP request to get a webpage or text data from a server file
|
||||
/// </summary>
|
||||
|
|
|
|||
23
MinecraftClient/Protocol/Handlers/Forge/FMLHandshakeClientState.cs
Executable file
23
MinecraftClient/Protocol/Handlers/Forge/FMLHandshakeClientState.cs
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.Forge
|
||||
{
|
||||
/// <summary>
|
||||
/// Copy of the forge enum for client states.
|
||||
/// https://github.com/MinecraftForge/MinecraftForge/blob/ebe9b6d4cbc4a5281c386994f1fbda04df5d2e1f/src/main/java/net/minecraftforge/fml/common/network/handshake/FMLHandshakeClientState.java
|
||||
/// </summary>
|
||||
enum FMLHandshakeClientState : byte
|
||||
{
|
||||
START,
|
||||
HELLO,
|
||||
WAITINGSERVERDATA,
|
||||
WAITINGSERVERCOMPLETE,
|
||||
PENDINGCOMPLETE,
|
||||
COMPLETE,
|
||||
DONE,
|
||||
ERROR
|
||||
}
|
||||
}
|
||||
21
MinecraftClient/Protocol/Handlers/Forge/FMLHandshakeDiscriminator.cs
Executable file
21
MinecraftClient/Protocol/Handlers/Forge/FMLHandshakeDiscriminator.cs
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.Forge
|
||||
{
|
||||
/// <summary>
|
||||
/// Different "discriminator byte" values for the forge handshake.
|
||||
/// https://github.com/MinecraftForge/MinecraftForge/blob/ebe9b6d4cbc4a5281c386994f1fbda04df5d2e1f/src/main/java/net/minecraftforge/fml/common/network/handshake/FMLHandshakeCodec.java
|
||||
/// </summary>
|
||||
enum FMLHandshakeDiscriminator : byte
|
||||
{
|
||||
ServerHello = 0,
|
||||
ClientHello = 1,
|
||||
ModList = 2,
|
||||
RegistryData = 3,
|
||||
HandshakeAck = 255, //-1
|
||||
HandshakeReset = 254, //-2
|
||||
}
|
||||
}
|
||||
70
MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs
Executable file
70
MinecraftClient/Protocol/Handlers/Forge/ForgeInfo.cs
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers.Forge
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about a modded server install.
|
||||
/// </summary>
|
||||
public class ForgeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an individual forge mod.
|
||||
/// </summary>
|
||||
public class ForgeMod
|
||||
{
|
||||
public ForgeMod(String ModID, String Version)
|
||||
{
|
||||
this.ModID = ModID;
|
||||
this.Version = Version;
|
||||
}
|
||||
|
||||
public readonly String ModID;
|
||||
public readonly String Version;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ModID + " v" + Version;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ForgeMod> Mods;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new ForgeInfo from the given data.
|
||||
/// </summary>
|
||||
/// <param name="data">The modinfo JSON tag.</param>
|
||||
internal ForgeInfo(Json.JSONData data)
|
||||
{
|
||||
// Example ModInfo (with spacing):
|
||||
|
||||
// "modinfo": {
|
||||
// "type": "FML",
|
||||
// "modList": [{
|
||||
// "modid": "mcp",
|
||||
// "version": "9.05"
|
||||
// }, {
|
||||
// "modid": "FML",
|
||||
// "version": "8.0.99.99"
|
||||
// }, {
|
||||
// "modid": "Forge",
|
||||
// "version": "11.14.3.1512"
|
||||
// }, {
|
||||
// "modid": "rpcraft",
|
||||
// "version": "Beta 1.3 - 1.8.0"
|
||||
// }]
|
||||
// }
|
||||
|
||||
this.Mods = new List<ForgeMod>();
|
||||
foreach (Json.JSONData mod in data.Properties["modList"].DataArray)
|
||||
{
|
||||
String modid = mod.Properties["modid"].StringValue;
|
||||
String version = mod.Properties["version"].StringValue;
|
||||
|
||||
this.Mods.Add(new ForgeMod(modid, version));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
179
MinecraftClient/Protocol/Handlers/Json.cs
Normal file
179
MinecraftClient/Protocol/Handlers/Json.cs
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// This class parses JSON data and returns an object describing that data.
|
||||
/// Really lightweight JSON handling by ORelio - (c) 2013 - 2014
|
||||
/// </summary>
|
||||
|
||||
static class Json
|
||||
{
|
||||
/// <summary>
|
||||
/// Parse some JSON and return the corresponding JSON object
|
||||
/// </summary>
|
||||
|
||||
public static JSONData ParseJson(string json)
|
||||
{
|
||||
int cursorpos = 0;
|
||||
return String2Data(json, ref cursorpos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The class storing unserialized JSON data
|
||||
/// The data can be an object, an array or a string
|
||||
/// </summary>
|
||||
|
||||
public 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>
|
||||
/// 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>
|
||||
|
||||
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] == '\\')
|
||||
{
|
||||
try //Unicode character \u0123
|
||||
{
|
||||
if (toparse[cursorpos + 1] == 'u'
|
||||
&& isHex(toparse[cursorpos + 2])
|
||||
&& isHex(toparse[cursorpos + 3])
|
||||
&& isHex(toparse[cursorpos + 4])
|
||||
&& isHex(toparse[cursorpos + 5]))
|
||||
{
|
||||
//"abc\u0123abc" => "0123" => 0123 => Unicode char n°0123 => Add char to string
|
||||
data.StringValue += char.ConvertFromUtf32(int.Parse(toparse.Substring(cursorpos + 2, 4), System.Globalization.NumberStyles.HexNumber));
|
||||
cursorpos += 6; continue;
|
||||
}
|
||||
else cursorpos++; //Normal character escapement \"
|
||||
}
|
||||
catch (IndexOutOfRangeException) { cursorpos++; } // \u01<end of string>
|
||||
catch (ArgumentOutOfRangeException) { cursorpos++; } // Unicode index 0123 was invalid
|
||||
}
|
||||
data.StringValue += toparse[cursorpos];
|
||||
cursorpos++;
|
||||
}
|
||||
cursorpos++;
|
||||
break;
|
||||
|
||||
//Number
|
||||
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.':
|
||||
data = new JSONData(JSONData.DataType.String);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while ((toparse[cursorpos] >= '0' && toparse[cursorpos] <= '9') || toparse[cursorpos] == '.')
|
||||
{
|
||||
sb.Append(toparse[cursorpos]);
|
||||
cursorpos++;
|
||||
}
|
||||
data.StringValue = sb.ToString();
|
||||
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);
|
||||
}
|
||||
while (cursorpos < toparse.Length
|
||||
&& (char.IsWhiteSpace(toparse[cursorpos])
|
||||
|| toparse[cursorpos] == '\r'
|
||||
|| toparse[cursorpos] == '\n'))
|
||||
cursorpos++;
|
||||
return data;
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
return new JSONData(JSONData.DataType.String);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Small function for checking if a char is an hexadecimal char (0-9 A-F a-f)
|
||||
/// </summary>
|
||||
/// <param name="c">Char to test</param>
|
||||
/// <returns>True if hexadecimal</returns>
|
||||
|
||||
private static bool isHex(char c) { return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')); }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ using System.Threading;
|
|||
using MinecraftClient.Crypto;
|
||||
using MinecraftClient.Proxy;
|
||||
using System.Security.Cryptography;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
|
|
@ -33,6 +34,12 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
this.c = Client;
|
||||
this.protocolversion = ProtocolVersion;
|
||||
this.handler = Handler;
|
||||
|
||||
if (Settings.TerrainAndMovements)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Terrain & Movements currently not handled for that MC version.");
|
||||
Settings.TerrainAndMovements = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Protocol16Handler(TcpClient Client)
|
||||
|
|
@ -42,19 +49,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
private void Updater()
|
||||
{
|
||||
int keep_alive_interval = 100;
|
||||
int keep_alive_timer = 100;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
keep_alive_timer--;
|
||||
if (keep_alive_timer <= 0)
|
||||
{
|
||||
Send(getPaddingPacket());
|
||||
keep_alive_timer = keep_alive_interval;
|
||||
}
|
||||
}
|
||||
while (Update());
|
||||
}
|
||||
|
|
@ -171,7 +170,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
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 0xFA: string channel = readNextString();
|
||||
byte[] payload = readNextByteArray();
|
||||
handler.OnPluginChannelMessage(channel, payload);
|
||||
break;
|
||||
case 0xFF: string reason = readNextString();
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, reason); break;
|
||||
default: return false; //unknown packet!
|
||||
|
|
@ -449,15 +451,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
byte[] token = readNextByteArray();
|
||||
|
||||
if (serverID == "-")
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Server is in offline mode.");
|
||||
return true; //No need to check session or start encryption
|
||||
}
|
||||
else
|
||||
{
|
||||
else if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Handshake successful. (Server ID: " + serverID + ')');
|
||||
return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey);
|
||||
}
|
||||
|
||||
return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
|
@ -467,7 +465,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey);
|
||||
byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
|
||||
|
||||
ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated.");
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated.");
|
||||
|
||||
if (serverIDhash != "-")
|
||||
{
|
||||
|
|
@ -504,7 +503,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (pid[0] == 0xFC)
|
||||
{
|
||||
readData(4);
|
||||
s = CryptoHandler.getAesStream(c.GetStream(), secretKey, this);
|
||||
s = CryptoHandler.getAesStream(c.GetStream(), secretKey);
|
||||
encrypted = true;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -513,7 +512,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
public bool Login()
|
||||
{
|
||||
if (Handshake(handler.getUserUUID(), handler.getUsername(), handler.getSessionID(), handler.getServerHost(), handler.getServerPort()))
|
||||
if (Handshake(handler.GetUserUUID(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort()))
|
||||
{
|
||||
Send(new byte[] { 0xCD, 0 });
|
||||
try
|
||||
|
|
@ -622,6 +621,41 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
catch (SocketException) { return false; }
|
||||
}
|
||||
|
||||
public bool SendBrandInfo(string brandInfo)
|
||||
{
|
||||
return false; //Only supported since MC 1.7
|
||||
}
|
||||
|
||||
public bool SendLocationUpdate(Location location, bool onGround)
|
||||
{
|
||||
return false; //Currently not implemented
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a plugin channel packet to the server.
|
||||
/// </summary>
|
||||
/// <param name="channel">Channel to send packet on</param>
|
||||
/// <param name="data">packet Data</param>
|
||||
|
||||
public bool SendPluginChannelPacket(string channel, byte[] data)
|
||||
{
|
||||
try {
|
||||
byte[] channelLength = BitConverter.GetBytes((short)channel.Length);
|
||||
Array.Reverse(channelLength);
|
||||
|
||||
byte[] channelData = Encoding.BigEndianUnicode.GetBytes(channel);
|
||||
|
||||
byte[] dataLength = BitConverter.GetBytes((short)data.Length);
|
||||
Array.Reverse(dataLength);
|
||||
|
||||
Send(concatBytes(new byte[] { 0xFA }, channelLength, channelData, dataLength, data));
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SocketException) { return false; }
|
||||
catch (System.IO.IOException) { return false; }
|
||||
}
|
||||
|
||||
public string AutoComplete(string BehindCursor)
|
||||
{
|
||||
if (String.IsNullOrEmpty(BehindCursor))
|
||||
|
|
@ -652,25 +686,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return result.ToArray();
|
||||
}
|
||||
|
||||
public byte[] getPaddingPacket()
|
||||
{
|
||||
//Will generate a 15-bytes long padding packet
|
||||
byte[] id = new byte[1] { 0xFA }; //Plugin Message
|
||||
byte[] channel_name = Encoding.BigEndianUnicode.GetBytes("MCC|");
|
||||
byte[] channel_name_len = BitConverter.GetBytes((short)channel_name.Length); Array.Reverse(channel_name_len);
|
||||
byte[] data = new byte[] { 0x00, 0x00 };
|
||||
byte[] data_len = BitConverter.GetBytes((short)data.Length); Array.Reverse(data_len);
|
||||
byte[] packet_data = concatBytes(id, channel_name_len, channel_name, data_len, data);
|
||||
return packet_data;
|
||||
}
|
||||
|
||||
public static bool doPing(string host, int port, ref int protocolversion)
|
||||
{
|
||||
try
|
||||
{
|
||||
string version = "";
|
||||
TcpClient tcp = ProxyHandler.newTcpClient(host, port);
|
||||
tcp.ReceiveTimeout = 5000; //MC 1.7.2+ SpigotMC servers won't answer, so we need a reasonable timeout.
|
||||
tcp.ReceiveTimeout = 5000; //MC 1.7.2+ SpigotMC servers won't respond, so we need a reasonable timeout.
|
||||
byte[] ping = new byte[2] { 0xfe, 0x01 };
|
||||
tcp.Client.Send(ping, SocketFlags.None);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,614 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using MinecraftClient.Crypto;
|
||||
using MinecraftClient.Proxy;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for Minecraft 1.7.X Protocol
|
||||
/// </summary>
|
||||
|
||||
class Protocol17Handler : IMinecraftCom
|
||||
{
|
||||
IMinecraftComHandler handler;
|
||||
private bool autocomplete_received = false;
|
||||
private string autocomplete_result = "";
|
||||
private bool encrypted = false;
|
||||
private int protocolversion;
|
||||
private Thread netRead;
|
||||
Crypto.IAesStream s;
|
||||
TcpClient c;
|
||||
|
||||
public Protocol17Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler)
|
||||
{
|
||||
ConsoleIO.SetAutoCompleteEngine(this);
|
||||
ChatParser.InitTranslations();
|
||||
this.c = Client;
|
||||
this.protocolversion = ProtocolVersion;
|
||||
this.handler = Handler;
|
||||
}
|
||||
|
||||
private Protocol17Handler(TcpClient Client)
|
||||
{
|
||||
this.c = Client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Separate thread. Network reading loop.
|
||||
/// </summary>
|
||||
|
||||
private void Updater()
|
||||
{
|
||||
int keep_alive_interval = 100;
|
||||
int keep_alive_timer = 100;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
keep_alive_timer--;
|
||||
if (keep_alive_timer <= 0)
|
||||
{
|
||||
Send(getPaddingPacket());
|
||||
keep_alive_timer = keep_alive_interval;
|
||||
}
|
||||
}
|
||||
while (Update());
|
||||
}
|
||||
catch (System.IO.IOException) { }
|
||||
catch (SocketException) { }
|
||||
catch (ObjectDisposedException) { }
|
||||
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read and data from the network. Should be called on a separate thread.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
|
||||
private bool Update()
|
||||
{
|
||||
handler.OnUpdate();
|
||||
if (c.Client == null || !c.Connected) { return false; }
|
||||
int id = 0, size = 0;
|
||||
try
|
||||
{
|
||||
while (c.Client.Available > 0)
|
||||
{
|
||||
size = readNextVarInt(); //Packet size
|
||||
id = readNextVarInt(); //Packet ID
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case 0x00:
|
||||
byte[] keepalive = new byte[4] { 0, 0, 0, 0 };
|
||||
Receive(keepalive, 0, 4, SocketFlags.None);
|
||||
byte[] keepalive_packet = concatBytes(getVarInt(0x00), keepalive);
|
||||
byte[] keepalive_tosend = concatBytes(getVarInt(keepalive_packet.Length), keepalive_packet);
|
||||
Send(keepalive_tosend);
|
||||
break;
|
||||
case 0x02:
|
||||
handler.OnTextReceived(ChatParser.ParseText(readNextString()));
|
||||
break;
|
||||
case 0x38:
|
||||
string name = readNextString();
|
||||
bool online = readNextBool();
|
||||
short ping = readNextShort();
|
||||
Guid FakeUUID = new Guid(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(name)).Take(16).ToArray());
|
||||
if (online)
|
||||
{
|
||||
handler.OnPlayerJoin(FakeUUID, name);
|
||||
}
|
||||
else handler.OnPlayerLeave(FakeUUID);
|
||||
break;
|
||||
case 0x3A:
|
||||
int autocomplete_count = readNextVarInt();
|
||||
string tab_list = "";
|
||||
for (int i = 0; i < autocomplete_count; i++)
|
||||
{
|
||||
autocomplete_result = readNextString();
|
||||
if (autocomplete_result != "")
|
||||
tab_list = tab_list + autocomplete_result + " ";
|
||||
}
|
||||
autocomplete_received = true;
|
||||
tab_list = tab_list.Trim();
|
||||
if (tab_list.Length > 0)
|
||||
ConsoleIO.WriteLineFormatted("§8" + tab_list, false);
|
||||
break;
|
||||
case 0x40:
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.InGameKick, ChatParser.ParseText(readNextString()));
|
||||
return false;
|
||||
default:
|
||||
readData(size - getVarInt(id).Length); //Skip packet
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the updating thread. Should be called after login success.
|
||||
/// </summary>
|
||||
|
||||
private void StartUpdating()
|
||||
{
|
||||
netRead = new Thread(new ThreadStart(Updater));
|
||||
netRead.Name = "ProtocolPacketHandler";
|
||||
netRead.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server, cancel network reading.
|
||||
/// </summary>
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (netRead != null)
|
||||
{
|
||||
netRead.Abort();
|
||||
c.Close();
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read some data and discard the result
|
||||
/// </summary>
|
||||
/// <param name="offset">Amount of bytes to read</param>
|
||||
|
||||
private void readData(int offset)
|
||||
{
|
||||
if (offset > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] cache = new byte[offset];
|
||||
Receive(cache, 0, offset, SocketFlags.None);
|
||||
}
|
||||
catch (OutOfMemoryException) { }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a string from the network
|
||||
/// </summary>
|
||||
/// <returns>The string</returns>
|
||||
|
||||
private string readNextString()
|
||||
{
|
||||
int length = readNextVarInt();
|
||||
if (length > 0)
|
||||
{
|
||||
byte[] cache = new byte[length];
|
||||
Receive(cache, 0, length, SocketFlags.None);
|
||||
return Encoding.UTF8.GetString(cache);
|
||||
}
|
||||
else return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a uuid from the network
|
||||
/// </summary>
|
||||
/// <param name="cache">Cache of bytes to read from</param>
|
||||
/// <returns>The uuid</returns>
|
||||
|
||||
private Guid readNextUUID()
|
||||
{
|
||||
byte[] cache = new byte[16];
|
||||
Receive(cache, 0, 16, SocketFlags.None);
|
||||
return new Guid(cache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a short from the network
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
|
||||
private short readNextShort()
|
||||
{
|
||||
byte[] tmp = new byte[2];
|
||||
Receive(tmp, 0, 2, SocketFlags.None);
|
||||
Array.Reverse(tmp);
|
||||
return BitConverter.ToInt16(tmp, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a boolean from the network
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
|
||||
private bool readNextBool()
|
||||
{
|
||||
byte[] tmp = new byte[1];
|
||||
Receive(tmp, 0, 1, SocketFlags.None);
|
||||
return tmp[0] != 0x00;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte array from the network
|
||||
/// </summary>
|
||||
/// <returns>The byte array</returns>
|
||||
|
||||
private byte[] readNextByteArray()
|
||||
{
|
||||
byte[] tmp = new byte[2];
|
||||
Receive(tmp, 0, 2, SocketFlags.None);
|
||||
Array.Reverse(tmp);
|
||||
short len = BitConverter.ToInt16(tmp, 0);
|
||||
byte[] data = new byte[len];
|
||||
Receive(data, 0, len, SocketFlags.None);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from the network
|
||||
/// </summary>
|
||||
/// <returns>The integer</returns>
|
||||
|
||||
private int readNextVarInt()
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
byte[] tmp = new byte[1];
|
||||
while (true)
|
||||
{
|
||||
Receive(tmp, 0, 1, SocketFlags.None);
|
||||
k = tmp[0];
|
||||
i |= (k & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
if ((k & 0x80) != 128) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build an integer for sending over the network
|
||||
/// </summary>
|
||||
/// <param name="paramInt">Integer to encode</param>
|
||||
/// <returns>Byte array for this integer</returns>
|
||||
|
||||
private static byte[] getVarInt(int paramInt)
|
||||
{
|
||||
List<byte> bytes = new List<byte>();
|
||||
while ((paramInt & -128) != 0)
|
||||
{
|
||||
bytes.Add((byte)(paramInt & 127 | 128));
|
||||
paramInt = (int)(((uint)paramInt) >> 7);
|
||||
}
|
||||
bytes.Add((byte)paramInt);
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Easily append several byte arrays
|
||||
/// </summary>
|
||||
/// <param name="bytes">Bytes to append</param>
|
||||
/// <returns>Array containing all the data</returns>
|
||||
|
||||
private static byte[] concatBytes(params byte[][] bytes)
|
||||
{
|
||||
List<byte> result = new List<byte>();
|
||||
foreach (byte[] array in bytes)
|
||||
result.AddRange(array);
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C-like atoi function for parsing an int from string
|
||||
/// </summary>
|
||||
/// <param name="str">String to parse</param>
|
||||
/// <returns>Int parsed</returns>
|
||||
|
||||
private static int atoi(string str)
|
||||
{
|
||||
return int.Parse(new string(str.Trim().TakeWhile(char.IsDigit).ToArray()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Network reading method. Read bytes from the socket or encrypted socket.
|
||||
/// </summary>
|
||||
|
||||
private void Receive(byte[] buffer, int start, int offset, SocketFlags f)
|
||||
{
|
||||
int read = 0;
|
||||
while (read < offset)
|
||||
{
|
||||
if (encrypted)
|
||||
{
|
||||
read += s.Read(buffer, start + read, offset - read);
|
||||
}
|
||||
else read += c.Client.Receive(buffer, start + read, offset - read, f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Network sending method. Send bytes using the socket or encrypted socket.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
|
||||
private void Send(byte[] buffer)
|
||||
{
|
||||
if (encrypted)
|
||||
{
|
||||
s.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
else c.Client.Send(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do the Minecraft login.
|
||||
/// </summary>
|
||||
/// <returns>True if login successful</returns>
|
||||
|
||||
public bool Login()
|
||||
{
|
||||
byte[] packet_id = getVarInt(0);
|
||||
byte[] protocol_version = getVarInt(protocolversion);
|
||||
byte[] server_adress_val = Encoding.UTF8.GetBytes(handler.getServerHost());
|
||||
byte[] server_adress_len = getVarInt(server_adress_val.Length);
|
||||
byte[] server_port = BitConverter.GetBytes((ushort)handler.getServerPort()); Array.Reverse(server_port);
|
||||
byte[] next_state = getVarInt(2);
|
||||
byte[] handshake_packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state);
|
||||
byte[] handshake_packet_tosend = concatBytes(getVarInt(handshake_packet.Length), handshake_packet);
|
||||
|
||||
Send(handshake_packet_tosend);
|
||||
|
||||
byte[] username_val = Encoding.UTF8.GetBytes(handler.getUsername());
|
||||
byte[] username_len = getVarInt(username_val.Length);
|
||||
byte[] login_packet = concatBytes(packet_id, username_len, username_val);
|
||||
byte[] login_packet_tosend = concatBytes(getVarInt(login_packet.Length), login_packet);
|
||||
|
||||
Send(login_packet_tosend);
|
||||
|
||||
readNextVarInt(); //Packet size
|
||||
int pid = readNextVarInt(); //Packet ID
|
||||
if (pid == 0x00) //Login rejected
|
||||
{
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString()));
|
||||
return false;
|
||||
}
|
||||
else if (pid == 0x01) //Encryption request
|
||||
{
|
||||
string serverID = readNextString();
|
||||
byte[] Serverkey = readNextByteArray();
|
||||
byte[] token = readNextByteArray();
|
||||
return StartEncryption(handler.getUserUUID(), handler.getSessionID(), token, serverID, Serverkey);
|
||||
}
|
||||
else if (pid == 0x02) //Login successful
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Server is in offline mode.");
|
||||
StartUpdating();
|
||||
return true; //No need to check session or start encryption
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start network encryption. Automatically called by Login() if the server requests encryption.
|
||||
/// </summary>
|
||||
/// <returns>True if encryption was successful</returns>
|
||||
|
||||
private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverKey)
|
||||
{
|
||||
System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey);
|
||||
byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
|
||||
|
||||
ConsoleIO.WriteLineFormatted("§8Crypto keys & hash generated.");
|
||||
|
||||
if (serverIDhash != "-")
|
||||
{
|
||||
Console.WriteLine("Checking Session...");
|
||||
if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey)))
|
||||
{
|
||||
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, "Failed to check session.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Encrypt the data
|
||||
byte[] key_enc = RSAService.Encrypt(secretKey, false);
|
||||
byte[] token_enc = RSAService.Encrypt(token, false);
|
||||
byte[] key_len = BitConverter.GetBytes((short)key_enc.Length); Array.Reverse(key_len);
|
||||
byte[] token_len = BitConverter.GetBytes((short)token_enc.Length); Array.Reverse(token_len);
|
||||
|
||||
//Encryption Response packet
|
||||
byte[] packet_id = getVarInt(0x01);
|
||||
byte[] encryption_response = concatBytes(packet_id, key_len, key_enc, token_len, token_enc);
|
||||
byte[] encryption_response_tosend = concatBytes(getVarInt(encryption_response.Length), encryption_response);
|
||||
Send(encryption_response_tosend);
|
||||
|
||||
//Start client-side encryption
|
||||
s = CryptoHandler.getAesStream(c.GetStream(), secretKey, this);
|
||||
encrypted = true;
|
||||
|
||||
//Read and skip the next packet
|
||||
int received_packet_size = readNextVarInt();
|
||||
int received_packet_id = readNextVarInt();
|
||||
bool encryption_success = (received_packet_id == 0x02);
|
||||
if (received_packet_id == 0) { handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, ChatParser.ParseText(readNextString())); }
|
||||
else readData(received_packet_size - getVarInt(received_packet_id).Length);
|
||||
if (encryption_success) { StartUpdating(); }
|
||||
return encryption_success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Useless padding packet for solving Mono issue.
|
||||
/// </summary>
|
||||
/// <returns>The padding packet</returns>
|
||||
|
||||
public byte[] getPaddingPacket()
|
||||
{
|
||||
//Will generate a 15-bytes long padding packet
|
||||
byte[] id = getVarInt(0x17); //Plugin Message
|
||||
byte[] channel_name = Encoding.UTF8.GetBytes("MCC|Pad");
|
||||
byte[] channel_name_len = getVarInt(channel_name.Length);
|
||||
byte[] data = new byte[] { 0x00, 0x00, 0x00 };
|
||||
byte[] data_len = BitConverter.GetBytes((short)data.Length); Array.Reverse(data_len);
|
||||
byte[] packet_data = concatBytes(id, channel_name_len, channel_name, data_len, data);
|
||||
byte[] packet_length = getVarInt(packet_data.Length);
|
||||
return concatBytes(packet_length, packet_data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message to the server
|
||||
/// </summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <returns>True if properly sent</returns>
|
||||
|
||||
public bool SendChatMessage(string message)
|
||||
{
|
||||
if (String.IsNullOrEmpty(message))
|
||||
return true;
|
||||
try
|
||||
{
|
||||
byte[] packet_id = getVarInt(0x01);
|
||||
byte[] message_val = Encoding.UTF8.GetBytes(message);
|
||||
byte[] message_len = getVarInt(message_val.Length);
|
||||
byte[] message_packet = concatBytes(packet_id, message_len, message_val);
|
||||
byte[] message_packet_tosend = concatBytes(getVarInt(message_packet.Length), message_packet);
|
||||
Send(message_packet_tosend);
|
||||
return true;
|
||||
}
|
||||
catch (SocketException) { return false; }
|
||||
catch (System.IO.IOException) { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a respawn packet to the server
|
||||
/// </summary>
|
||||
/// <param name="message">Message</param>
|
||||
/// <returns>True if properly sent</returns>
|
||||
|
||||
public bool SendRespawnPacket()
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] packet_id = getVarInt(0x16);
|
||||
byte[] action_id = new byte[] { 0 };
|
||||
byte[] respawn_packet = concatBytes(getVarInt(packet_id.Length + 1), packet_id, action_id);
|
||||
Send(respawn_packet);
|
||||
return true;
|
||||
}
|
||||
catch (SocketException) { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server
|
||||
/// </summary>
|
||||
/// <param name="message">Optional disconnect reason</param>
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] packet_id = getVarInt(0x40);
|
||||
byte[] message_val = Encoding.UTF8.GetBytes("\"disconnect.quitting\"");
|
||||
byte[] message_len = getVarInt(message_val.Length);
|
||||
byte[] disconnect_packet = concatBytes(packet_id, message_len, message_val);
|
||||
byte[] disconnect_packet_tosend = concatBytes(getVarInt(disconnect_packet.Length), disconnect_packet);
|
||||
Send(disconnect_packet_tosend);
|
||||
}
|
||||
catch (SocketException) { }
|
||||
catch (System.IO.IOException) { }
|
||||
catch (NullReferenceException) { }
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Autocomplete text while typing username or command
|
||||
/// </summary>
|
||||
/// <param name="BehindCursor">Text behind cursor</param>
|
||||
/// <returns>Completed text</returns>
|
||||
|
||||
public string AutoComplete(string BehindCursor)
|
||||
{
|
||||
if (String.IsNullOrEmpty(BehindCursor))
|
||||
return "";
|
||||
|
||||
byte[] packet_id = getVarInt(0x14);
|
||||
byte[] tocomplete_val = Encoding.UTF8.GetBytes(BehindCursor);
|
||||
byte[] tocomplete_len = getVarInt(tocomplete_val.Length);
|
||||
byte[] tabcomplete_packet = concatBytes(packet_id, tocomplete_len, tocomplete_val);
|
||||
byte[] tabcomplete_packet_tosend = concatBytes(getVarInt(tabcomplete_packet.Length), tabcomplete_packet);
|
||||
|
||||
autocomplete_received = false;
|
||||
autocomplete_result = BehindCursor;
|
||||
Send(tabcomplete_packet_tosend);
|
||||
|
||||
int wait_left = 50; //do not wait more than 5 seconds (50 * 100 ms)
|
||||
while (wait_left > 0 && !autocomplete_received) { System.Threading.Thread.Sleep(100); wait_left--; }
|
||||
return autocomplete_result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping a Minecraft server to get information about the server
|
||||
/// </summary>
|
||||
/// <returns>True if ping was successful</returns>
|
||||
|
||||
public static bool doPing(string host, int port, ref int protocolversion)
|
||||
{
|
||||
string version = "";
|
||||
TcpClient tcp = ProxyHandler.newTcpClient(host, port);
|
||||
tcp.ReceiveBufferSize = 1024 * 1024;
|
||||
|
||||
byte[] packet_id = getVarInt(0);
|
||||
byte[] protocol_version = getVarInt(4);
|
||||
byte[] server_adress_val = Encoding.UTF8.GetBytes(host);
|
||||
byte[] server_adress_len = getVarInt(server_adress_val.Length);
|
||||
byte[] server_port = BitConverter.GetBytes((ushort)port); Array.Reverse(server_port);
|
||||
byte[] next_state = getVarInt(1);
|
||||
byte[] packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state);
|
||||
byte[] tosend = concatBytes(getVarInt(packet.Length), packet);
|
||||
|
||||
tcp.Client.Send(tosend, SocketFlags.None);
|
||||
|
||||
byte[] status_request = getVarInt(0);
|
||||
byte[] request_packet = concatBytes(getVarInt(status_request.Length), status_request);
|
||||
|
||||
tcp.Client.Send(request_packet, SocketFlags.None);
|
||||
|
||||
Protocol17Handler ComTmp = new Protocol17Handler(tcp);
|
||||
if (ComTmp.readNextVarInt() > 0) //Read Response length
|
||||
{
|
||||
if (ComTmp.readNextVarInt() == 0x00) //Read Packet ID
|
||||
{
|
||||
string result = ComTmp.readNextString(); //Get the Json data
|
||||
if (result[0] == '{' && result.Contains("protocol\":") && result.Contains("name\":\""))
|
||||
{
|
||||
string[] tmp_ver = result.Split(new string[] { "protocol\":" }, StringSplitOptions.None);
|
||||
string[] tmp_name = result.Split(new string[] { "name\":\"" }, StringSplitOptions.None);
|
||||
|
||||
if (tmp_ver.Length >= 2 && tmp_name.Length >= 2)
|
||||
{
|
||||
protocolversion = atoi(tmp_ver[1]);
|
||||
|
||||
//Handle if "name" exists twice, eg when connecting to a server with another user logged in.
|
||||
version = (tmp_name.Length == 2) ? tmp_name[1].Split('"')[0] : tmp_name[2].Split('"')[0];
|
||||
|
||||
//Automatic fix for BungeeCord 1.8 not properly reporting protocol version
|
||||
if (protocolversion < 47 && version.Split(' ').Contains("1.8"))
|
||||
protocolversion = ProtocolHandler.MCVer2ProtocolVersion("1.8.0");
|
||||
|
||||
ConsoleIO.WriteLineFormatted("§8Server version : " + version + " (protocol v" + protocolversion + ").");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Crypto;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
|
|
@ -13,7 +14,7 @@ namespace MinecraftClient.Protocol
|
|||
/// The protocol handler will take care of parsing and building the appropriate network packets.
|
||||
/// </summary>
|
||||
|
||||
public interface IMinecraftCom : IDisposable, IAutoComplete, IPaddingProvider
|
||||
public interface IMinecraftCom : IDisposable, IAutoComplete
|
||||
{
|
||||
/// <summary>
|
||||
/// Start the login procedure once connected to the server
|
||||
|
|
@ -43,5 +44,31 @@ namespace MinecraftClient.Protocol
|
|||
/// <returns>True if packet successfully sent</returns>
|
||||
|
||||
bool SendRespawnPacket();
|
||||
|
||||
/// <summary>
|
||||
/// Inform the server of the client being used to connect
|
||||
/// </summary>
|
||||
/// <param name="brandInfo">Client string describing the client</param>
|
||||
/// <returns>True if brand info was successfully sent</returns>
|
||||
|
||||
bool SendBrandInfo(string brandInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Send a location update telling that we moved to that location
|
||||
/// </summary>
|
||||
/// <param name="location">The new location</param>
|
||||
/// <returns>True if packet was successfully sent</returns>
|
||||
|
||||
bool SendLocationUpdate(Location location, bool onGround);
|
||||
|
||||
/// <summary>
|
||||
/// Send a plugin channel packet to the server.
|
||||
/// </summary>
|
||||
/// <see href="http://dinnerbone.com/blog/2012/01/13/minecraft-plugin-channels-messaging/" />
|
||||
/// <param name="channel">Channel to send packet on</param>
|
||||
/// <param name="data">packet Data</param>
|
||||
/// <returns>True if message was successfully sent</returns>
|
||||
|
||||
bool SendPluginChannelPacket(string channel, byte[] data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Mapping;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
|
|
@ -13,15 +14,23 @@ namespace MinecraftClient.Protocol
|
|||
|
||||
public interface IMinecraftComHandler
|
||||
{
|
||||
/* The MinecraftCom Hanler must
|
||||
/* The MinecraftCom Handler must
|
||||
* provide these getters */
|
||||
|
||||
int getServerPort();
|
||||
string getServerHost();
|
||||
string getUsername();
|
||||
string getUserUUID();
|
||||
string getSessionID();
|
||||
string[] getOnlinePlayers();
|
||||
int GetServerPort();
|
||||
string GetServerHost();
|
||||
string GetUsername();
|
||||
string GetUserUUID();
|
||||
string GetSessionID();
|
||||
string[] GetOnlinePlayers();
|
||||
Location GetCurrentLocation();
|
||||
World GetWorld();
|
||||
|
||||
/// <summary>
|
||||
/// Called when a server was successfully joined
|
||||
/// </summary>
|
||||
|
||||
void OnGameJoined();
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when the protocol handler receives a chat message
|
||||
|
|
@ -44,6 +53,13 @@ namespace MinecraftClient.Protocol
|
|||
|
||||
void OnPlayerLeave(Guid uuid);
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server sets the new location for the player
|
||||
/// </summary>
|
||||
/// <param name="location">New location of the player</param>
|
||||
|
||||
void UpdateLocation(Location location);
|
||||
|
||||
/// <summary>
|
||||
/// This method is called when the connection has been lost
|
||||
/// </summary>
|
||||
|
|
@ -56,5 +72,40 @@ namespace MinecraftClient.Protocol
|
|||
/// </summary>
|
||||
|
||||
void OnUpdate();
|
||||
|
||||
/// <summary>
|
||||
/// Registers the given plugin channel for the given bot.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to register.</param>
|
||||
/// <param name="bot">The bot to register the channel for.</param>
|
||||
|
||||
void RegisterPluginChannel(string channel, ChatBot bot);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the given plugin channel for the given bot.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to unregister.</param>
|
||||
/// <param name="bot">The bot to unregister the channel for.</param>
|
||||
|
||||
void UnregisterPluginChannel(string channel, ChatBot bot);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a plugin channel packet to the server.
|
||||
/// See http://wiki.vg/Plugin_channel for more information about plugin channels.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to send the packet on.</param>
|
||||
/// <param name="data">The payload for the packet.</param>
|
||||
/// <param name="sendEvenIfNotRegistered">Whether the packet should be sent even if the server or the client hasn't registered it yet.</param>
|
||||
/// <returns>Whether the packet was sent: true if it was sent, false if there was a connection error or it wasn't registered.</returns>
|
||||
|
||||
bool SendPluginChannelMessage(string channel, byte[] data, bool sendEvenIfNotRegistered = false);
|
||||
|
||||
/// <summary>
|
||||
/// Called when a plugin channel message was sent from the server.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel the message was sent on</param>
|
||||
/// <param name="data">The data from the channel</param>
|
||||
|
||||
void OnPluginChannelMessage(string channel, byte[] data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,291 +1,440 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol.Handlers;
|
||||
using MinecraftClient.Proxy;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.Security;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle login, session, server ping and provide a protocol handler for interacting with a minecraft server.
|
||||
/// </summary>
|
||||
|
||||
public static class ProtocolHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve information about a Minecraft server
|
||||
/// </summary>
|
||||
/// <param name="serverIP">Server IP to ping</param>
|
||||
/// <param name="serverPort">Server Port to ping</param>
|
||||
/// <param name="protocolversion">Will contain protocol version, if ping successful</param>
|
||||
/// <returns>TRUE if ping was successful</returns>
|
||||
|
||||
public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversion))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (Protocol17Handler.doPing(serverIP, serverPort, ref protocolversion))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Unexpected answer from the server (is that a Minecraft server ?)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8An error occured while attempting to connect to this IP.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a protocol handler for the specified Minecraft version
|
||||
/// </summary>
|
||||
/// <param name="Client">Tcp Client connected to the server</param>
|
||||
/// <param name="ProtocolVersion">Protocol version to handle</param>
|
||||
/// <param name="Handler">Handler with the appropriate callbacks</param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static IMinecraftCom getProtocolHandler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler)
|
||||
{
|
||||
int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 };
|
||||
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
|
||||
return new Protocol16Handler(Client, ProtocolVersion, Handler);
|
||||
int[] supportedVersions_Protocol17 = { 4, 5 };
|
||||
if (Array.IndexOf(supportedVersions_Protocol17, ProtocolVersion) > -1)
|
||||
return new Protocol17Handler(Client, ProtocolVersion, Handler);
|
||||
int[] supportedVersions_Protocol18 = { 47 };
|
||||
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
|
||||
return new Protocol18Handler(Client, ProtocolVersion, Handler);
|
||||
throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a human-readable Minecraft version number to network protocol version number
|
||||
/// </summary>
|
||||
/// <param name="MCVersion">The Minecraft version number</param>
|
||||
/// <returns>The protocol version number or 0 if could not determine protocol version: error, unknown, not supported</returns>
|
||||
|
||||
public static int MCVer2ProtocolVersion(string MCVersion)
|
||||
{
|
||||
if (MCVersion.Contains('.'))
|
||||
{
|
||||
switch (MCVersion.Split(' ')[0].Trim())
|
||||
{
|
||||
case "1.4.6":
|
||||
case "1.4.7":
|
||||
return 51;
|
||||
case "1.5.1":
|
||||
return 60;
|
||||
case "1.5.2":
|
||||
return 61;
|
||||
case "1.6.0":
|
||||
return 72;
|
||||
case "1.6.1":
|
||||
case "1.6.2":
|
||||
case "1.6.3":
|
||||
case "1.6.4":
|
||||
return 73;
|
||||
case "1.7.2":
|
||||
case "1.7.3":
|
||||
case "1.7.4":
|
||||
case "1.7.5":
|
||||
return 4;
|
||||
case "1.7.6":
|
||||
case "1.7.7":
|
||||
case "1.7.8":
|
||||
case "1.7.9":
|
||||
case "1.7.10":
|
||||
return 5;
|
||||
case "1.8.0":
|
||||
case "1.8.1":
|
||||
return 47;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return Int32.Parse(MCVersion);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium };
|
||||
|
||||
/// <summary>
|
||||
/// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme.
|
||||
/// </summary>
|
||||
/// <param name="user">Login</param>
|
||||
/// <param name="pass">Password</param>
|
||||
/// <param name="accesstoken">Will contain the access token returned by Minecraft.net, if the login is successful</param>
|
||||
/// <param name="uuid">Will contain the player's UUID, needed for multiplayer</param>
|
||||
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
|
||||
|
||||
public static LoginResult GetLogin(ref string user, string pass, ref string accesstoken, ref string uuid)
|
||||
{
|
||||
try
|
||||
{
|
||||
string result = "";
|
||||
string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + jsonEncode(user) + "\", \"password\": \"" + jsonEncode(pass) + "\" }";
|
||||
int code = doHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result);
|
||||
if (code == 200)
|
||||
{
|
||||
if (result.Contains("availableProfiles\":[]}"))
|
||||
{
|
||||
return LoginResult.NotPremium;
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { accesstoken = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { user = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "availableProfiles\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { uuid = temp[1].Split('"')[0]; }
|
||||
return LoginResult.Success;
|
||||
}
|
||||
}
|
||||
else if (code == 403)
|
||||
{
|
||||
if (result.Contains("UserMigratedException"))
|
||||
{
|
||||
return LoginResult.AccountMigrated;
|
||||
}
|
||||
else return LoginResult.WrongPassword;
|
||||
}
|
||||
else if (code == 503)
|
||||
{
|
||||
return LoginResult.ServiceUnavailable;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Got error code from server: " + code);
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
catch (System.Security.Authentication.AuthenticationException)
|
||||
{
|
||||
return LoginResult.SSLError;
|
||||
}
|
||||
catch (System.IO.IOException e)
|
||||
{
|
||||
if (e.Message.Contains("authentication"))
|
||||
{
|
||||
return LoginResult.SSLError;
|
||||
}
|
||||
else return LoginResult.OtherError;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check session using Mojang's Yggdrasil authentication scheme. Allows to join an online-mode server
|
||||
/// </summary>
|
||||
/// <param name="user">Username</param>
|
||||
/// <param name="accesstoken">Session ID</param>
|
||||
/// <param name="serverhash">Server ID</param>
|
||||
/// <returns>TRUE if session was successfully checked</returns>
|
||||
|
||||
public static bool SessionCheck(string uuid, string accesstoken, string serverhash)
|
||||
{
|
||||
try
|
||||
{
|
||||
string result = "";
|
||||
string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + "\",\"serverId\":\"" + serverhash + "\"}";
|
||||
int code = doHTTPSPost("sessionserver.mojang.com", "/session/minecraft/join", json_request, ref result);
|
||||
return (result == "");
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manual HTTPS request since we must directly use a TcpClient because of the proxy.
|
||||
/// This method connects to the server, enables SSL, do the request and read the response.
|
||||
/// </summary>
|
||||
/// <param name="host">Host to connect to</param>
|
||||
/// <param name="endpoint">Endpoint for making the request</param>
|
||||
/// <param name="request">Request payload</param>
|
||||
/// <param name="result">Request result</param>
|
||||
/// <returns>HTTP Status code</returns>
|
||||
|
||||
private static int doHTTPSPost(string host, string endpoint, string request, ref string result)
|
||||
{
|
||||
string postResult = null;
|
||||
int statusCode = 520;
|
||||
AutoTimeout.Perform(() =>
|
||||
{
|
||||
TcpClient client = ProxyHandler.newTcpClient(host, 443);
|
||||
SslStream stream = new SslStream(client.GetStream());
|
||||
stream.AuthenticateAsClient(host);
|
||||
|
||||
List<String> http_request = new List<string>();
|
||||
http_request.Add("POST " + endpoint + " HTTP/1.1");
|
||||
http_request.Add("Host: " + host);
|
||||
http_request.Add("User-Agent: MCC/" + Program.Version);
|
||||
http_request.Add("Content-Type: application/json");
|
||||
http_request.Add("Content-Length: " + Encoding.ASCII.GetBytes(request).Length);
|
||||
http_request.Add("Connection: close");
|
||||
http_request.Add("");
|
||||
http_request.Add(request);
|
||||
|
||||
stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", http_request.ToArray())));
|
||||
System.IO.StreamReader sr = new System.IO.StreamReader(stream);
|
||||
string raw_result = sr.ReadToEnd();
|
||||
|
||||
if (raw_result.StartsWith("HTTP/1.1"))
|
||||
{
|
||||
postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4);
|
||||
statusCode = Settings.str2int(raw_result.Split(' ')[1]);
|
||||
}
|
||||
else statusCode = 520; //Web server is returning an unknown error
|
||||
}, 15000);
|
||||
result = postResult;
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode a string to a json string.
|
||||
/// Will convert special chars to \u0000 unicode escape sequences.
|
||||
/// </summary>
|
||||
/// <param name="text">Source text</param>
|
||||
/// <returns>Encoded text</returns>
|
||||
|
||||
private static string jsonEncode(string text)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
foreach (char c in text)
|
||||
{
|
||||
if (char.IsLetterOrDigit(c))
|
||||
{
|
||||
result.Append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Append("\\u");
|
||||
result.Append(((int)c).ToString("x4"));
|
||||
}
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MinecraftClient.Protocol.Handlers;
|
||||
using MinecraftClient.Proxy;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.Security;
|
||||
using MinecraftClient.Protocol.Handlers.Forge;
|
||||
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle login, session, server ping and provide a protocol handler for interacting with a minecraft server.
|
||||
/// </summary>
|
||||
|
||||
public static class ProtocolHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve information about a Minecraft server
|
||||
/// </summary>
|
||||
/// <param name="serverIP">Server IP to ping</param>
|
||||
/// <param name="serverPort">Server Port to ping</param>
|
||||
/// <param name="protocolversion">Will contain protocol version, if ping successful</param>
|
||||
/// <returns>TRUE if ping was successful</returns>
|
||||
|
||||
public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo forgeInfo)
|
||||
{
|
||||
bool success = false;
|
||||
int protocolversionTmp = 0;
|
||||
ForgeInfo forgeInfoTmp = null;
|
||||
if (AutoTimeout.Perform(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Protocol16Handler.doPing(serverIP, serverPort, ref protocolversionTmp)
|
||||
|| Protocol18Handler.doPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp))
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
else ConsoleIO.WriteLineFormatted("§8Unexpected response from the server (is that a Minecraft server?)");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message));
|
||||
}
|
||||
}, TimeSpan.FromSeconds(30)))
|
||||
{
|
||||
protocolversion = protocolversionTmp;
|
||||
forgeInfo = forgeInfoTmp;
|
||||
return success;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8A timeout occured while attempting to connect to this IP.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a protocol handler for the specified Minecraft version
|
||||
/// </summary>
|
||||
/// <param name="Client">Tcp Client connected to the server</param>
|
||||
/// <param name="ProtocolVersion">Protocol version to handle</param>
|
||||
/// <param name="Handler">Handler with the appropriate callbacks</param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static IMinecraftCom getProtocolHandler(TcpClient Client, int ProtocolVersion, ForgeInfo forgeInfo, IMinecraftComHandler Handler)
|
||||
{
|
||||
int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 };
|
||||
if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1)
|
||||
return new Protocol16Handler(Client, ProtocolVersion, Handler);
|
||||
int[] supportedVersions_Protocol18 = { 4, 5, 47, 107 };
|
||||
if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1)
|
||||
return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo);
|
||||
throw new NotSupportedException("The protocol version no." + ProtocolVersion + " is not supported.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a human-readable Minecraft version number to network protocol version number
|
||||
/// </summary>
|
||||
/// <param name="MCVersion">The Minecraft version number</param>
|
||||
/// <returns>The protocol version number or 0 if could not determine protocol version: error, unknown, not supported</returns>
|
||||
|
||||
public static int MCVer2ProtocolVersion(string MCVersion)
|
||||
{
|
||||
if (MCVersion.Contains('.'))
|
||||
{
|
||||
switch (MCVersion.Split(' ')[0].Trim())
|
||||
{
|
||||
case "1.4.6":
|
||||
case "1.4.7":
|
||||
return 51;
|
||||
case "1.5.1":
|
||||
return 60;
|
||||
case "1.5.2":
|
||||
return 61;
|
||||
case "1.6.0":
|
||||
return 72;
|
||||
case "1.6.1":
|
||||
case "1.6.2":
|
||||
case "1.6.3":
|
||||
case "1.6.4":
|
||||
return 73;
|
||||
case "1.7.2":
|
||||
case "1.7.3":
|
||||
case "1.7.4":
|
||||
case "1.7.5":
|
||||
return 4;
|
||||
case "1.7.6":
|
||||
case "1.7.7":
|
||||
case "1.7.8":
|
||||
case "1.7.9":
|
||||
case "1.7.10":
|
||||
return 5;
|
||||
case "1.8.0":
|
||||
case "1.8.1":
|
||||
case "1.8.2":
|
||||
case "1.8.3":
|
||||
case "1.8.4":
|
||||
case "1.8.5":
|
||||
case "1.8.6":
|
||||
case "1.8.7":
|
||||
case "1.8.8":
|
||||
case "1.8.9":
|
||||
return 47;
|
||||
case "1.9.0":
|
||||
return 107;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return Int32.Parse(MCVersion);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, NullError };
|
||||
|
||||
/// <summary>
|
||||
/// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme.
|
||||
/// </summary>
|
||||
/// <param name="user">Login</param>
|
||||
/// <param name="pass">Password</param>
|
||||
/// <param name="accesstoken">Will contain the access token returned by Minecraft.net, if the login is successful</param>
|
||||
/// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param>
|
||||
/// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param>
|
||||
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
|
||||
|
||||
public static LoginResult GetLogin(string user, string pass, out SessionToken session)
|
||||
{
|
||||
session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") };
|
||||
|
||||
try
|
||||
{
|
||||
string result = "";
|
||||
|
||||
string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + jsonEncode(user) + "\", \"password\": \"" + jsonEncode(pass) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }";
|
||||
int code = doHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result);
|
||||
if (code == 200)
|
||||
{
|
||||
if (result.Contains("availableProfiles\":[]}"))
|
||||
{
|
||||
return LoginResult.NotPremium;
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { session.ID = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { session.PlayerName = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "availableProfiles\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { session.PlayerID = temp[1].Split('"')[0]; }
|
||||
return LoginResult.Success;
|
||||
}
|
||||
}
|
||||
else if (code == 403)
|
||||
{
|
||||
if (result.Contains("UserMigratedException"))
|
||||
{
|
||||
return LoginResult.AccountMigrated;
|
||||
}
|
||||
else return LoginResult.WrongPassword;
|
||||
}
|
||||
else if (code == 503)
|
||||
{
|
||||
return LoginResult.ServiceUnavailable;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Got error code from server: " + code);
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
catch (System.Security.Authentication.AuthenticationException)
|
||||
{
|
||||
return LoginResult.SSLError;
|
||||
}
|
||||
catch (System.IO.IOException e)
|
||||
{
|
||||
if (e.Message.Contains("authentication"))
|
||||
{
|
||||
return LoginResult.SSLError;
|
||||
}
|
||||
else return LoginResult.OtherError;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates whether accessToken must be refreshed
|
||||
/// </summary>
|
||||
/// <param name="accesstoken">Will contain the cached access token previously returned by Minecraft.net</param>
|
||||
/// <param name="clienttoken">Will contain the cached client token created on login</param>
|
||||
/// <returns>Returns the status of the token (Valid, Invalid, etc.)</returns>
|
||||
///
|
||||
public static LoginResult GetTokenValidation(SessionToken session)
|
||||
{
|
||||
try
|
||||
{
|
||||
string result = "";
|
||||
string json_request = "{\"accessToken\": \"" + jsonEncode(session.ID) + "\", \"clientToken\": \"" + jsonEncode(session.ClientID) + "\" }";
|
||||
int code = doHTTPSPost("authserver.mojang.com", "/validate", json_request, ref result);
|
||||
if (code == 204)
|
||||
{
|
||||
return LoginResult.Success;
|
||||
}
|
||||
else if (code == 403)
|
||||
{
|
||||
return LoginResult.LoginRequired;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes invalid token
|
||||
/// </summary>
|
||||
/// <param name="user">Login</param>
|
||||
/// <param name="accesstoken">Will contain the new access token returned by Minecraft.net, if the refresh is successful</param>
|
||||
/// <param name="clienttoken">Will contain the client token generated before sending to Minecraft.net</param>
|
||||
/// <param name="uuid">Will contain the player's PlayerID, needed for multiplayer</param>
|
||||
/// <returns>Returns the status of the new token request (Success, Failure, etc.)</returns>
|
||||
///
|
||||
public static LoginResult GetNewToken(SessionToken currentsession, out SessionToken newsession)
|
||||
{
|
||||
newsession = new SessionToken();
|
||||
try
|
||||
{
|
||||
string result = "";
|
||||
string json_request = "{ \"accessToken\": \"" + jsonEncode(currentsession.ID) + "\", \"clientToken\": \"" + jsonEncode(currentsession.ClientID) + "\", \"selectedProfile\": { \"id\": \"" + jsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + jsonEncode(currentsession.PlayerName) + "\" } }";
|
||||
int code = doHTTPSPost("authserver.mojang.com", "/refresh", json_request, ref result);
|
||||
if (code == 200)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return LoginResult.NullError;
|
||||
}
|
||||
else {
|
||||
string[] temp = result.Split(new string[] { "accessToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { newsession.ID = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "clientToken\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { newsession.ClientID = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "name\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { newsession.PlayerName = temp[1].Split('"')[0]; }
|
||||
temp = result.Split(new string[] { "selectedProfile\":[{\"id\":\"" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (temp.Length >= 2) { newsession.PlayerID = temp[1].Split('"')[0]; }
|
||||
return LoginResult.Success;
|
||||
}
|
||||
}
|
||||
else if (code == 403 && result.Contains("InvalidToken"))
|
||||
{
|
||||
return LoginResult.InvalidToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleIO.WriteLineFormatted("§8Got error code from server while refreshing authentication: " + code);
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return LoginResult.OtherError;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check session using Mojang's Yggdrasil authentication scheme. Allows to join an online-mode server
|
||||
/// </summary>
|
||||
/// <param name="user">Username</param>
|
||||
/// <param name="accesstoken">Session ID</param>
|
||||
/// <param name="serverhash">Server ID</param>
|
||||
/// <returns>TRUE if session was successfully checked</returns>
|
||||
|
||||
public static bool SessionCheck(string uuid, string accesstoken, string serverhash)
|
||||
{
|
||||
try
|
||||
{
|
||||
string result = "";
|
||||
string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + "\",\"serverId\":\"" + serverhash + "\"}";
|
||||
int code = doHTTPSPost("sessionserver.mojang.com", "/session/minecraft/join", json_request, ref result);
|
||||
return (result == "");
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
public static void RealmsListWorlds(string username, string uuid, string accesstoken)
|
||||
{
|
||||
string result = "";
|
||||
string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, Program.MCHighestVersion);
|
||||
doHTTPSGet("mcoapi.minecraft.net", "/worlds", cookies, ref result);
|
||||
Console.WriteLine(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a HTTPS GET request to the specified endpoint of the Mojang API
|
||||
/// </summary>
|
||||
/// <param name="host">Host to connect to</param>
|
||||
/// <param name="endpoint">Endpoint for making the request</param>
|
||||
/// <param name="cookies">Cookies for making the request</param>
|
||||
/// <param name="result">Request result</param>
|
||||
/// <returns>HTTP Status code</returns>
|
||||
|
||||
private static int doHTTPSGet(string host, string endpoint, string cookies, ref string result)
|
||||
{
|
||||
List<String> http_request = new List<string>();
|
||||
http_request.Add("GET " + endpoint + " HTTP/1.1");
|
||||
http_request.Add("Cookie: " + cookies);
|
||||
http_request.Add("Cache-Control: no-cache");
|
||||
http_request.Add("Pragma: no-cache");
|
||||
http_request.Add("Host: " + host);
|
||||
http_request.Add("User-Agent: Java/1.6.0_27");
|
||||
http_request.Add("Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7");
|
||||
http_request.Add("Connection: close");
|
||||
http_request.Add("");
|
||||
return doHTTPSRequest(http_request, host, ref result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a HTTPS POST request to the specified endpoint of the Mojang API
|
||||
/// </summary>
|
||||
/// <param name="host">Host to connect to</param>
|
||||
/// <param name="endpoint">Endpoint for making the request</param>
|
||||
/// <param name="request">Request payload</param>
|
||||
/// <param name="result">Request result</param>
|
||||
/// <returns>HTTP Status code</returns>
|
||||
|
||||
private static int doHTTPSPost(string host, string endpoint, string request, ref string result)
|
||||
{
|
||||
List<String> http_request = new List<string>();
|
||||
http_request.Add("POST " + endpoint + " HTTP/1.1");
|
||||
http_request.Add("Host: " + host);
|
||||
http_request.Add("User-Agent: MCC/" + Program.Version);
|
||||
http_request.Add("Content-Type: application/json");
|
||||
http_request.Add("Content-Length: " + Encoding.ASCII.GetBytes(request).Length);
|
||||
http_request.Add("Connection: close");
|
||||
http_request.Add("");
|
||||
http_request.Add(request);
|
||||
return doHTTPSRequest(http_request, host, ref result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manual HTTPS request since we must directly use a TcpClient because of the proxy.
|
||||
/// This method connects to the server, enables SSL, do the request and read the response.
|
||||
/// </summary>
|
||||
/// <param name="headers">Request headers and optional body (POST)</param>
|
||||
/// <param name="host">Host to connect to</param>
|
||||
/// <param name="result">Request result</param>
|
||||
/// <returns>HTTP Status code</returns>
|
||||
|
||||
private static int doHTTPSRequest(List<string> headers, string host, ref string result)
|
||||
{
|
||||
string postResult = null;
|
||||
int statusCode = 520;
|
||||
AutoTimeout.Perform(() =>
|
||||
{
|
||||
TcpClient client = ProxyHandler.newTcpClient(host, 443, true);
|
||||
SslStream stream = new SslStream(client.GetStream());
|
||||
stream.AuthenticateAsClient(host);
|
||||
stream.Write(Encoding.ASCII.GetBytes(String.Join("\r\n", headers.ToArray())));
|
||||
System.IO.StreamReader sr = new System.IO.StreamReader(stream);
|
||||
string raw_result = sr.ReadToEnd();
|
||||
if (raw_result.StartsWith("HTTP/1.1"))
|
||||
{
|
||||
postResult = raw_result.Substring(raw_result.IndexOf("\r\n\r\n") + 4);
|
||||
statusCode = Settings.str2int(raw_result.Split(' ')[1]);
|
||||
}
|
||||
else statusCode = 520; //Web server is returning an unknown error
|
||||
}, TimeSpan.FromSeconds(30));
|
||||
result = postResult;
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode a string to a json string.
|
||||
/// Will convert special chars to \u0000 unicode escape sequences.
|
||||
/// </summary>
|
||||
/// <param name="text">Source text</param>
|
||||
/// <returns>Encoded text</returns>
|
||||
|
||||
private static string jsonEncode(string text)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
foreach (char c in text)
|
||||
{
|
||||
if ((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z'))
|
||||
{
|
||||
result.Append(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AppendFormat(@"\u{0:x4}", (int)c);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
MinecraftClient/Protocol/SessionCache/CacheType.cs
Normal file
4
MinecraftClient/Protocol/SessionCache/CacheType.cs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace MinecraftClient.Protocol.SessionCache
|
||||
{
|
||||
public enum CacheType { None, Memory, Disk };
|
||||
}
|
||||
177
MinecraftClient/Protocol/SessionCache/SessionCache.cs
Normal file
177
MinecraftClient/Protocol/SessionCache/SessionCache.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
using MinecraftClient.Protocol;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Timers;
|
||||
|
||||
namespace MinecraftClient.Protocol.SessionCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle sessions caching and storage.
|
||||
/// </summary>
|
||||
|
||||
public static class SessionCache
|
||||
{
|
||||
private const string SessionCacheFile = "SessionCache.db";
|
||||
|
||||
private static Dictionary<string, SessionToken> sessions = new Dictionary<string, SessionToken>();
|
||||
private static FileSystemWatcher cachemonitor = new FileSystemWatcher();
|
||||
private static Timer updatetimer = new Timer(100);
|
||||
private static List<KeyValuePair<string, SessionToken>> pendingadds = new List<KeyValuePair<string, SessionToken>>();
|
||||
|
||||
private static BinaryFormatter formatter = new BinaryFormatter();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve whether SessionCache contains a session for the given login.
|
||||
/// </summary>
|
||||
/// <param name="login">User login used with Minecraft.net</param>
|
||||
/// <returns>TRUE if session is available</returns>
|
||||
|
||||
public static bool Contains(string login)
|
||||
{
|
||||
return sessions.ContainsKey(login);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store a session and save it to disk if required.
|
||||
/// </summary>
|
||||
/// <param name="login">User login used with Minecraft.net</param>
|
||||
/// <param name="session">User session token used with Minecraft.net</param>
|
||||
|
||||
public static void Store(string login, SessionToken session)
|
||||
{
|
||||
if (Contains(login))
|
||||
{
|
||||
sessions[login] = session;
|
||||
}
|
||||
else
|
||||
{
|
||||
sessions.Add(login, session);
|
||||
}
|
||||
|
||||
if (Settings.SessionCaching == CacheType.Disk && updatetimer.Enabled == true)
|
||||
{
|
||||
pendingadds.Add(new KeyValuePair<string, SessionToken>(login, session));
|
||||
}
|
||||
else if (Settings.SessionCaching == CacheType.Disk)
|
||||
{
|
||||
SaveToDisk();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a session token for the given login.
|
||||
/// </summary>
|
||||
/// <param name="login">User login used with Minecraft.net</param>
|
||||
/// <returns>SessionToken for given login</returns>
|
||||
|
||||
public static SessionToken Get(string login)
|
||||
{
|
||||
return sessions[login];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize cache monitoring to keep cache updated with external changes.
|
||||
/// </summary>
|
||||
/// <returns>TRUE if session tokens are seeded from file</returns>
|
||||
|
||||
public static bool InitializeDiskCache()
|
||||
{
|
||||
cachemonitor.Path = AppDomain.CurrentDomain.BaseDirectory;
|
||||
cachemonitor.IncludeSubdirectories = false;
|
||||
cachemonitor.Filter = SessionCacheFile;
|
||||
cachemonitor.NotifyFilter = NotifyFilters.LastWrite;
|
||||
cachemonitor.Changed += new FileSystemEventHandler(OnChanged);
|
||||
cachemonitor.EnableRaisingEvents = true;
|
||||
|
||||
updatetimer.Elapsed += HandlePending;
|
||||
|
||||
return LoadFromDisk();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads cache on external cache file change.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender</param>
|
||||
/// <param name="e">Event data</param>
|
||||
|
||||
private static void OnChanged(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
updatetimer.Stop();
|
||||
updatetimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after timer elapsed. Reads disk cache and adds new/modified sessions back.
|
||||
/// </summary>
|
||||
/// <param name="sender">Sender</param>
|
||||
/// <param name="e">Event data</param>
|
||||
|
||||
private static void HandlePending(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
LoadFromDisk();
|
||||
|
||||
foreach(KeyValuePair<string, SessionToken> pending in pendingadds.ToArray())
|
||||
{
|
||||
Store(pending.Key, pending.Value);
|
||||
pendingadds.Remove(pending);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads cache file and loads SessionTokens into SessionCache.
|
||||
/// </summary>
|
||||
/// <returns>True if data is successfully loaded</returns>
|
||||
|
||||
private static bool LoadFromDisk()
|
||||
{
|
||||
if (File.Exists(SessionCacheFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream(SessionCacheFile, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
sessions = (Dictionary<string, SessionToken>)formatter.Deserialize(fs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine("Error reading cached sessions from disk: " + ex.Message);
|
||||
}
|
||||
catch (SerializationException)
|
||||
{
|
||||
Console.WriteLine("Malformed sessions from cache file ");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves SessionToken's from SessionCache into cache file.
|
||||
/// </summary>
|
||||
|
||||
private static void SaveToDisk()
|
||||
{
|
||||
bool fileexists = File.Exists(SessionCacheFile);
|
||||
|
||||
using (FileStream fs = new FileStream(SessionCacheFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
cachemonitor.EnableRaisingEvents = false;
|
||||
|
||||
// delete existing file contents
|
||||
if (fileexists)
|
||||
{
|
||||
fs.SetLength(0);
|
||||
fs.Flush();
|
||||
}
|
||||
|
||||
formatter.Serialize(fs, sessions);
|
||||
cachemonitor.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
21
MinecraftClient/Protocol/SessionToken.cs
Normal file
21
MinecraftClient/Protocol/SessionToken.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
|
||||
namespace MinecraftClient.Protocol
|
||||
{
|
||||
[Serializable]
|
||||
public class SessionToken
|
||||
{
|
||||
public string ID { get; set; }
|
||||
public string PlayerName { get; set; }
|
||||
public string PlayerID { get; set; }
|
||||
public string ClientID { get; set; }
|
||||
|
||||
public SessionToken()
|
||||
{
|
||||
ID = String.Empty;
|
||||
PlayerName = String.Empty;
|
||||
PlayerID = String.Empty;
|
||||
ClientID = String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,12 +24,15 @@ namespace MinecraftClient.Proxy
|
|||
/// <summary>
|
||||
/// Create a regular TcpClient or a proxied TcpClient according to the app Settings.
|
||||
/// </summary>
|
||||
/// <param name="host">Target host</param>
|
||||
/// <param name="port">Target port</param>
|
||||
/// <param name="login">True if the purpose is logging in to a Minecraft account</param>
|
||||
|
||||
public static TcpClient newTcpClient(string host, int port)
|
||||
public static TcpClient newTcpClient(string host, int port, bool login = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Settings.ProxyEnabled)
|
||||
if (login ? Settings.ProxyEnabledLogin : Settings.ProxyEnabledIngame)
|
||||
{
|
||||
ProxyType innerProxytype = ProxyType.Http;
|
||||
|
||||
|
|
@ -61,7 +64,7 @@ namespace MinecraftClient.Proxy
|
|||
{
|
||||
ConsoleIO.WriteLineFormatted("§8" + e.Message);
|
||||
proxy = null;
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using MinecraftClient.Protocol.SessionCache;
|
||||
|
||||
namespace MinecraftClient
|
||||
{
|
||||
|
|
@ -13,6 +15,9 @@ namespace MinecraftClient
|
|||
|
||||
public static class Settings
|
||||
{
|
||||
//Minecraft Console Client client information used for BrandInfo setting
|
||||
private const string MCCBrandInfo = "Minecraft-Console-Client/" + Program.Version;
|
||||
|
||||
//Main Settings.
|
||||
//Login: Username or email adress used as login for Minecraft/Mojang account
|
||||
//Username: The actual username of the user, obtained after login to the account
|
||||
|
|
@ -21,12 +26,13 @@ namespace MinecraftClient
|
|||
public static string Password = "";
|
||||
public static string ServerIP = "";
|
||||
public static ushort ServerPort = 25565;
|
||||
public static string ServerVersion = "";
|
||||
public static string ServerVersion = "";
|
||||
public static string SingleCommand = "";
|
||||
public static string ConsoleTitle = "";
|
||||
|
||||
//Proxy Settings
|
||||
public static bool ProxyEnabled = false;
|
||||
public static bool ProxyEnabledLogin = false;
|
||||
public static bool ProxyEnabledIngame = false;
|
||||
public static string ProxyHost = "";
|
||||
public static int ProxyPort = 0;
|
||||
public static Proxy.ProxyHandler.Type proxyType = Proxy.ProxyHandler.Type.HTTP;
|
||||
|
|
@ -34,16 +40,26 @@ namespace MinecraftClient
|
|||
public static string ProxyPassword = "";
|
||||
|
||||
//Other Settings
|
||||
public static string TranslationsFile_FromMCDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\.minecraft\assets\objects\9e\9e2fdc43fc1c7024ff5922b998fadb2971a64ee0"; //MC 1.7.4 en_GB.lang
|
||||
public static string TranslationsFile_Website_Index = "https://s3.amazonaws.com/Minecraft.Download/indexes/1.7.4.json";
|
||||
public static string TranslationsFile_FromMCDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\.minecraft\assets\objects\3d\3d7f778ea0a3baaf826ae75a094d77c46410902f"; //MC 1.9 en_GB.lang
|
||||
public static string TranslationsFile_Website_Index = "https://s3.amazonaws.com/Minecraft.Download/indexes/1.9.json";
|
||||
public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net";
|
||||
public static TimeSpan splitMessageDelay = TimeSpan.FromSeconds(2);
|
||||
public static List<string> Bots_Owners = new List<string>();
|
||||
public static TimeSpan botMessageDelay = TimeSpan.FromSeconds(2);
|
||||
public static string Language = "en_GB";
|
||||
public static bool chatTimeStamps = false;
|
||||
public static bool exitOnFailure = false;
|
||||
public static bool interactiveMode = true;
|
||||
public static char internalCmdChar = '/';
|
||||
public static bool playerHeadAsIcon = false;
|
||||
public static string chatbotLogFile = "";
|
||||
public static bool CacheScripts = true;
|
||||
public static string BrandInfo = MCCBrandInfo;
|
||||
public static bool DisplaySystemMessages = true;
|
||||
public static bool DisplayXPBarMessages = true;
|
||||
public static bool TerrainAndMovements = false;
|
||||
public static string PrivateMsgsCmdName = "tell";
|
||||
public static CacheType SessionCaching = CacheType.None;
|
||||
public static bool DebugMessages = false;
|
||||
|
||||
//AntiAFK Settings
|
||||
public static bool AntiAFK_Enabled = false;
|
||||
|
|
@ -88,12 +104,22 @@ namespace MinecraftClient
|
|||
public static bool RemoteCtrl_AutoTpaccept = true;
|
||||
public static bool RemoteCtrl_AutoTpaccept_Everyone = false;
|
||||
|
||||
//Custom app variables and Minecraft accounts
|
||||
private static Dictionary<string, string> AppVars = new Dictionary<string, string>();
|
||||
private static Dictionary<string, KeyValuePair<string, string>> Accounts = new Dictionary<string, KeyValuePair<string, string>>();
|
||||
private static Dictionary<string, KeyValuePair<string, ushort>> Servers = new Dictionary<string, KeyValuePair<string, ushort>>();
|
||||
//Chat Message Parsing
|
||||
public static bool ChatFormat_Builtins = true;
|
||||
public static Regex ChatFormat_Public = null;
|
||||
public static Regex ChatFormat_Private = null;
|
||||
public static Regex ChatFormat_TeleportRequest = null;
|
||||
|
||||
private enum ParseMode { Default, Main, AppVars, Proxy, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl };
|
||||
//Auto Respond
|
||||
public static bool AutoRespond_Enabled = false;
|
||||
public static string AutoRespond_Matches = "matches.ini";
|
||||
|
||||
//Custom app variables and Minecraft accounts
|
||||
private static readonly Dictionary<string, object> AppVars = new Dictionary<string, object>();
|
||||
private static readonly Dictionary<string, KeyValuePair<string, string>> Accounts = new Dictionary<string, KeyValuePair<string, string>>();
|
||||
private static readonly Dictionary<string, KeyValuePair<string, ushort>> Servers = new Dictionary<string, KeyValuePair<string, ushort>>();
|
||||
|
||||
private enum ParseMode { Default, Main, AppVars, Proxy, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond };
|
||||
|
||||
/// <summary>
|
||||
/// Load settings from the give INI file
|
||||
|
|
@ -127,6 +153,8 @@ namespace MinecraftClient
|
|||
case "remotecontrol": pMode = ParseMode.RemoteControl; break;
|
||||
case "proxy": pMode = ParseMode.Proxy; break;
|
||||
case "appvars": pMode = ParseMode.AppVars; break;
|
||||
case "autorespond": pMode = ParseMode.AutoRespond; break;
|
||||
case "chatformat": pMode = ParseMode.ChatFormat; break;
|
||||
default: pMode = ParseMode.Default; break;
|
||||
}
|
||||
}
|
||||
|
|
@ -143,15 +171,23 @@ namespace MinecraftClient
|
|||
{
|
||||
case "login": Login = argValue; break;
|
||||
case "password": Password = argValue; break;
|
||||
case "serverip": setServerIP(argValue); break;
|
||||
case "serverip": SetServerIP(argValue); break;
|
||||
case "singlecommand": SingleCommand = argValue; break;
|
||||
case "language": Language = argValue; break;
|
||||
case "consoletitle": ConsoleTitle = argValue; break;
|
||||
case "timestamps": chatTimeStamps = str2bool(argValue); break;
|
||||
case "exitonfailure": exitOnFailure = str2bool(argValue); break;
|
||||
case "exitonfailure": interactiveMode = !str2bool(argValue); break;
|
||||
case "playerheadicon": playerHeadAsIcon = str2bool(argValue); break;
|
||||
case "chatbotlogfile": chatbotLogFile = argValue; break;
|
||||
case "mcversion": ServerVersion = argValue; break;
|
||||
case "splitmessagedelay": splitMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break;
|
||||
case "scriptcache": CacheScripts = str2bool(argValue); break;
|
||||
case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break;
|
||||
case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break;
|
||||
case "terrainandmovements": TerrainAndMovements = str2bool(argValue); break;
|
||||
case "privatemsgscmdname": PrivateMsgsCmdName = argValue.ToLower().Trim(); break;
|
||||
case "botmessagedelay": botMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break;
|
||||
case "debugmessages": DebugMessages = str2bool(argValue); break;
|
||||
|
||||
case "botowners":
|
||||
Bots_Owners.Clear();
|
||||
|
|
@ -168,6 +204,12 @@ namespace MinecraftClient
|
|||
}
|
||||
break;
|
||||
|
||||
case "sessioncache":
|
||||
if (argValue == "none") { SessionCaching = CacheType.None; }
|
||||
else if (argValue == "memory") { SessionCaching = CacheType.Memory; }
|
||||
else if (argValue == "disk") { SessionCaching = CacheType.Disk; }
|
||||
break;
|
||||
|
||||
case "accountlist":
|
||||
if (File.Exists(argValue))
|
||||
{
|
||||
|
|
@ -197,16 +239,25 @@ namespace MinecraftClient
|
|||
if (server_data.Length == 2
|
||||
&& server_data[0] != "localhost"
|
||||
&& !server_data[0].Contains('.')
|
||||
&& setServerIP(server_data[1]))
|
||||
&& SetServerIP(server_data[1]))
|
||||
Servers[server_data[0]]
|
||||
= new KeyValuePair<string, ushort>(ServerIP, ServerPort);
|
||||
}
|
||||
|
||||
|
||||
//Restore current server info
|
||||
ServerIP = server_host_temp;
|
||||
ServerPort = server_port_temp;
|
||||
}
|
||||
break;
|
||||
|
||||
case "brandinfo":
|
||||
switch (argValue.Trim().ToLower())
|
||||
{
|
||||
case "mcc": BrandInfo = MCCBrandInfo; break;
|
||||
case "vanilla": BrandInfo = "vanilla"; break;
|
||||
default: BrandInfo = null; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -276,15 +327,29 @@ namespace MinecraftClient
|
|||
}
|
||||
break;
|
||||
|
||||
case ParseMode.ChatFormat:
|
||||
switch (argName.ToLower())
|
||||
{
|
||||
case "builtins": ChatFormat_Builtins = str2bool(argValue); break;
|
||||
case "public": ChatFormat_Public = new Regex(argValue); break;
|
||||
case "private": ChatFormat_Private = new Regex(argValue); break;
|
||||
case "tprequest": ChatFormat_TeleportRequest = new Regex(argValue); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ParseMode.Proxy:
|
||||
switch (argName.ToLower())
|
||||
{
|
||||
case "enabled": ProxyEnabled = str2bool(argValue); break;
|
||||
case "enabled":
|
||||
ProxyEnabledLogin = ProxyEnabledIngame = str2bool(argValue);
|
||||
if (argValue.Trim().ToLower() == "login")
|
||||
ProxyEnabledLogin = true;
|
||||
break;
|
||||
case "type":
|
||||
argValue = argValue.ToLower();
|
||||
if (argValue == "http") { proxyType = Proxy.ProxyHandler.Type.HTTP; }
|
||||
else if (argValue == "socks4") { proxyType = Proxy.ProxyHandler.Type.SOCKS4; }
|
||||
else if (argValue == "socks4a"){ proxyType = Proxy.ProxyHandler.Type.SOCKS4a;}
|
||||
else if (argValue == "socks4a") { proxyType = Proxy.ProxyHandler.Type.SOCKS4a; }
|
||||
else if (argValue == "socks5") { proxyType = Proxy.ProxyHandler.Type.SOCKS5; }
|
||||
break;
|
||||
case "server":
|
||||
|
|
@ -306,7 +371,15 @@ namespace MinecraftClient
|
|||
break;
|
||||
|
||||
case ParseMode.AppVars:
|
||||
setVar(argName, argValue);
|
||||
SetVar(argName, argValue);
|
||||
break;
|
||||
|
||||
case ParseMode.AutoRespond:
|
||||
switch (argName.ToLower())
|
||||
{
|
||||
case "enabled": AutoRespond_Enabled = str2bool(argValue); break;
|
||||
case "matchesfile": AutoRespond_Matches = argValue; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -342,14 +415,24 @@ namespace MinecraftClient
|
|||
+ "\r\n"
|
||||
+ "language=en_GB\r\n"
|
||||
+ "botowners=Player1,Player2,Player3\r\n"
|
||||
+ "consoletitle=%username% - Minecraft Console Client\r\n"
|
||||
+ "consoletitle=%username%@%serverip% - Minecraft Console Client\r\n"
|
||||
+ "internalcmdchar=slash #use 'none', 'slash' or 'backslash'\r\n"
|
||||
+ "splitmessagedelay=2 #seconds between each part of a long message\r\n"
|
||||
+ "botmessagedelay=2 #seconds to delay between message a bot makes to avoid accidental spam\r\n"
|
||||
+ "mcversion=auto #use 'auto' or '1.X.X' values\r\n"
|
||||
+ "brandinfo=mcc #use 'mcc','vanilla', or 'none'\r\n"
|
||||
+ "chatbotlogfile= #leave empty for no logfile\r\n"
|
||||
+ "privatemsgscmdname=tell #used by RemoteControl bot\r\n"
|
||||
+ "showsystemmessages=true #system messages for server ops\r\n"
|
||||
+ "showxpbarmessages=true #messages displayed above xp bar\r\n"
|
||||
+ "terrainandmovements=false #uses more ram, cpu, bandwidth\r\n"
|
||||
+ "sessioncache=memory #use 'none', 'memory' or 'disk'\r\n"
|
||||
+ "accountlist=accounts.txt\r\n"
|
||||
+ "serverlist=servers.txt\r\n"
|
||||
+ "playerheadicon=true\r\n"
|
||||
+ "exitonfailure=false\r\n"
|
||||
+ "debugmessages=false\r\n"
|
||||
+ "scriptcache=true\r\n"
|
||||
+ "timestamps=false\r\n"
|
||||
+ "\r\n"
|
||||
+ "[AppVars]\r\n"
|
||||
|
|
@ -358,12 +441,18 @@ namespace MinecraftClient
|
|||
+ "#%username% and %serverip% are reserved variables.\r\n"
|
||||
+ "\r\n"
|
||||
+ "[Proxy]\r\n"
|
||||
+ "enabled=false\r\n"
|
||||
+ "enabled=false #use 'false', 'true', or 'login' for login only\r\n"
|
||||
+ "type=HTTP #Supported types: HTTP, SOCKS4, SOCKS4a, SOCKS5\r\n"
|
||||
+ "server=0.0.0.0:0000\r\n"
|
||||
+ "username=\r\n"
|
||||
+ "password=\r\n"
|
||||
+ "\r\n"
|
||||
+ "[ChatFormat]\r\n"
|
||||
+ "builtins=true #support for handling vanilla and common message formats\r\n"
|
||||
+ "#public=^<([a-zA-Z0-9_]+)> (.+)$ #uncomment and adapt if necessary\r\n"
|
||||
+ "#private=^([a-zA-Z0-9_]+) whispers to you: (.+)$ #vanilla example\r\n"
|
||||
+ "#tprequest=^([a-zA-Z0-9_]+) has requested (?:to|that you) teleport to (?:you|them)\\.$\r\n"
|
||||
+ "\r\n"
|
||||
+ "#Bot Settings\r\n"
|
||||
+ "\r\n"
|
||||
+ "[Alerts]\r\n"
|
||||
|
|
@ -402,18 +491,48 @@ namespace MinecraftClient
|
|||
+ "[RemoteControl]\r\n"
|
||||
+ "enabled=false\r\n"
|
||||
+ "autotpaccept=true\r\n"
|
||||
+ "tpaccepteveryone=false\r\n", Encoding.UTF8);
|
||||
+ "tpaccepteveryone=false\r\n"
|
||||
+ "\r\n"
|
||||
+ "[AutoRespond]\r\n"
|
||||
+ "enabled=false\r\n"
|
||||
+ "matchesfile=matches.ini\r\n", Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static int str2int(string str) { try { return Convert.ToInt32(str); } catch { return 0; } }
|
||||
public static bool str2bool(string str) { return str == "true" || str == "1"; }
|
||||
/// <summary>
|
||||
/// Convert the specified string to an integer, defaulting to zero if invalid argument
|
||||
/// </summary>
|
||||
/// <param name="str">String to parse as an integer</param>
|
||||
/// <returns>Integer value</returns>
|
||||
|
||||
public static int str2int(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ToInt32(str);
|
||||
}
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the specified string to a boolean value, defaulting to false if invalid argument
|
||||
/// </summary>
|
||||
/// <param name="str">String to parse as a boolean</param>
|
||||
/// <returns>Boolean value</returns>
|
||||
|
||||
public static bool str2bool(string str)
|
||||
{
|
||||
if (String.IsNullOrEmpty(str))
|
||||
return false;
|
||||
str = str.Trim().ToLowerInvariant();
|
||||
return str == "true" || str == "1";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load login/password using an account alias
|
||||
/// </summary>
|
||||
/// <returns>True if the account was found and loaded</returns>
|
||||
|
||||
public static bool setAccount(string accountAlias)
|
||||
public static bool SetAccount(string accountAlias)
|
||||
{
|
||||
accountAlias = accountAlias.ToLower();
|
||||
if (Accounts.ContainsKey(accountAlias))
|
||||
|
|
@ -430,13 +549,13 @@ namespace MinecraftClient
|
|||
/// </summary>
|
||||
/// <returns>True if the server IP was valid and loaded, false otherwise</returns>
|
||||
|
||||
public static bool setServerIP(string server)
|
||||
public static bool SetServerIP(string server)
|
||||
{
|
||||
server = server.ToLower();
|
||||
string[] sip = server.Split(':');
|
||||
string host = sip[0];
|
||||
ushort port = 25565;
|
||||
|
||||
|
||||
if (sip.Length > 1)
|
||||
{
|
||||
try
|
||||
|
|
@ -448,17 +567,19 @@ namespace MinecraftClient
|
|||
|
||||
if (host == "localhost" || host.Contains('.'))
|
||||
{
|
||||
//Server IP (IP or domain names contains at least a dot)
|
||||
ServerIP = host;
|
||||
ServerPort = port;
|
||||
return true;
|
||||
}
|
||||
else if (Servers.ContainsKey(server))
|
||||
{
|
||||
//Server Alias (if no dot then treat the server as an alias)
|
||||
ServerIP = Servers[server].Key;
|
||||
ServerPort = Servers[server].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -469,15 +590,31 @@ namespace MinecraftClient
|
|||
/// <param name="varData">Value of the variable</param>
|
||||
/// <returns>True if the parameters were valid</returns>
|
||||
|
||||
public static bool setVar(string varName, string varData)
|
||||
public static bool SetVar(string varName, object varData)
|
||||
{
|
||||
varName = new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray()).ToLower();
|
||||
if (varName.Length > 0)
|
||||
lock (AppVars)
|
||||
{
|
||||
AppVars[varName] = varData;
|
||||
return true;
|
||||
varName = new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray()).ToLower();
|
||||
if (varName.Length > 0)
|
||||
{
|
||||
AppVars[varName] = varData;
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a custom %variable% or null if the variable does not exist
|
||||
/// </summary>
|
||||
/// <param name="varName">Variable name</param>
|
||||
/// <returns>The value or null if the variable does not exists</returns>
|
||||
|
||||
public static object GetVar(string varName)
|
||||
{
|
||||
if (AppVars.ContainsKey(varName))
|
||||
return AppVars[varName];
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -486,7 +623,7 @@ namespace MinecraftClient
|
|||
/// <param name="str">String to parse</param>
|
||||
/// <returns>Modifier string</returns>
|
||||
|
||||
public static string expandVars(string str)
|
||||
public static string ExpandVars(string str)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
|
|
@ -521,7 +658,7 @@ namespace MinecraftClient
|
|||
default:
|
||||
if (AppVars.ContainsKey(varname_lower))
|
||||
{
|
||||
result.Append(AppVars[varname_lower]);
|
||||
result.Append(AppVars[varname_lower].ToString());
|
||||
}
|
||||
else result.Append("%" + varname + '%');
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
==================================================================
|
||||
Minecraft Client v1.8.1 for Minecraft 1.4.6 to 1.8.0 - By ORelio
|
||||
==================================================================
|
||||
Minecraft Client v1.8.2 for Minecraft 1.4.6 to 1.8.3 - By ORelio
|
||||
==================================================================
|
||||
|
||||
Thanks for dowloading Minecraft Console Client!
|
||||
|
|
@ -15,6 +15,7 @@ in a fast and easy way without having to open the main Minecraft game.
|
|||
First, extract the archive if not already extracted.
|
||||
On Windows, simply open MinecraftClient.exe by double-clicking on it.
|
||||
On Mac or Linux, open a terminal in this folder and run "mono MinecraftClient.exe".
|
||||
If you cannot authenticate on Mono, you'll need to run "mozroots --import --ask-remove" once.
|
||||
|
||||
===========================================
|
||||
Using Configuration files & Enabling bots
|
||||
|
|
@ -118,6 +119,15 @@ These files describe how some messages should be printed depending on your prefe
|
|||
The client will automatically load en_GB.lang from your Minecraft folder if Minecraft is installed on your
|
||||
computer, or download it from Mojang's servers. You may choose another language in the config file.
|
||||
|
||||
=========================
|
||||
Detecting chat messages
|
||||
=========================
|
||||
|
||||
Minecraft Console Client can parse messages from the server in order to detect private and public messages.
|
||||
This is useful for reacting to messages eg when using the AutoRespond, Hangman game, or RemoteControl bots.
|
||||
However, for unusual chat formats, so you may need to tinker with the ChatFormat section of the config file.
|
||||
Building regular expressions can be a bit tricky, so you might want to try them out eg on regex101.com
|
||||
|
||||
======================
|
||||
Using the Alerts bot
|
||||
======================
|
||||
|
|
@ -165,6 +175,14 @@ You can remotely send chat messages or commands using /tell <yourbot> send <thet
|
|||
Remote control system can auto-accept /tpa and /tpahere requests from the bot owners.
|
||||
Auto-accept can be disabled or extended to requests from anyone in remote control configuration.
|
||||
|
||||
===============================
|
||||
Using the AutoRespond feature
|
||||
===============================
|
||||
|
||||
The AutoRespond bot allows you to automatically react on specific chat messages or server announcements.
|
||||
You can use either a string to detect in chat messages, or an advanced regular expression.
|
||||
For more information about how to define match rules, please refer to sample-matches.ini
|
||||
|
||||
=========================
|
||||
Disclaimer & Last words
|
||||
=========================
|
||||
|
|
|
|||
44
MinecraftClient/config/sample-matches.ini
Normal file
44
MinecraftClient/config/sample-matches.ini
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Minecraft Console Client
|
||||
# AutoRespond matches
|
||||
# Example config file
|
||||
|
||||
# Structure of a match: [Match] Followed by the match and action
|
||||
# The match can be a simple match or an advanced regular expression
|
||||
# You can use $u for username of the player triggering the match
|
||||
# You can define an action if the match was in a private message
|
||||
# You can define an action if the match was not sent by a player
|
||||
# Regex matches are also supported eg $1, $2, $3.. in actions
|
||||
|
||||
# Simple example: Respond to a message containing a keyword
|
||||
|
||||
[Match]
|
||||
match=hi
|
||||
action=send hi, $u!
|
||||
actionprivate=send /tell $u Hello!
|
||||
actionother=log detected "hi" message
|
||||
|
||||
# You do not need to specify all the "action" fields
|
||||
# Only one of them is required for each match
|
||||
|
||||
# Advanced example: Use a regular expression
|
||||
# Here a "regex" field is used instead of "match" field
|
||||
# Do not use both "regex" and "match" fields...
|
||||
|
||||
[Match]
|
||||
regex=^.*hello ([a-zA-Z0-9_]+).*$
|
||||
action=send hello too, $1!
|
||||
|
||||
# Example of using a script
|
||||
|
||||
[Match]
|
||||
match=dotest
|
||||
action=script test
|
||||
|
||||
# Example of matching a server announcement
|
||||
|
||||
[Match]
|
||||
match=server is restarting
|
||||
actionother=script restart
|
||||
|
||||
# Enjoy!
|
||||
# - ORelio
|
||||
31
MinecraftClient/config/sample-script-extended.cs
Normal file
31
MinecraftClient/config/sample-script-extended.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
//MCCScript 1.0
|
||||
|
||||
/* This script demonstrates how to use methods and arguments */
|
||||
|
||||
string text = "hello";
|
||||
|
||||
if (args.Length > 0)
|
||||
text = args[0];
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int count = MCC.GetVarAsInt("test") + 1;
|
||||
MCC.SetVar("test", count);
|
||||
SendHelloWorld(count, text);
|
||||
SleepBetweenSends();
|
||||
}
|
||||
|
||||
//MCCScript Extensions
|
||||
|
||||
/* Here you can define methods for use into your script */
|
||||
|
||||
void SendHelloWorld(int count, string text)
|
||||
{
|
||||
MCC.SendText("Hello World no. " + count + ": " + text);
|
||||
}
|
||||
|
||||
void SleepBetweenSends()
|
||||
{
|
||||
MCC.LogToConsole("Sleeping for 5 seconds...");
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
35
MinecraftClient/config/sample-script-with-chatbot.cs
Normal file
35
MinecraftClient/config/sample-script-with-chatbot.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
//MCCScript 1.0
|
||||
|
||||
/* This is a sample script that will load a ChatBot into Minecraft Console Client
|
||||
* Simply execute the script once with /script or the script scheduler to load the bot */
|
||||
|
||||
MCC.LoadBot(new ExampleBot());
|
||||
|
||||
//MCCScript Extensions
|
||||
|
||||
/* The ChatBot class must be defined as an extension of the script in the Extensions section
|
||||
* The class can override common methods from ChatBot.cs, take a look at MCC's source code */
|
||||
|
||||
public class ExampleBot : ChatBot
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
LogToConsole("Sucessfully Initialized!");
|
||||
}
|
||||
|
||||
public override void GetText(string text)
|
||||
{
|
||||
string message = "";
|
||||
string username = "";
|
||||
text = GetVerbatim(text);
|
||||
|
||||
if (IsChatMessage(text, ref message, ref username))
|
||||
{
|
||||
LogToConsole("Public message from " + username + ": " + message);
|
||||
}
|
||||
else if (IsPrivateMessage(text, ref message, ref username))
|
||||
{
|
||||
LogToConsole("Private message from " + username + ": " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
61
MinecraftClient/config/sample-script-with-world-access.cs
Normal file
61
MinecraftClient/config/sample-script-with-world-access.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//MCCScript 1.0
|
||||
|
||||
MCC.LoadBot(new WatchLamp());
|
||||
|
||||
//MCCScript Extensions
|
||||
|
||||
/* The ChatBot will access the world on a regular basis to watch for a lamp.
|
||||
* This is an example of how the world around the player can be accessed from a C# script. */
|
||||
|
||||
class WatchLamp : ChatBot
|
||||
{
|
||||
/* == CONFIG == */
|
||||
|
||||
int lampX = 0;
|
||||
int lampY = 64;
|
||||
int lampZ = 0;
|
||||
|
||||
/* == CODE == */
|
||||
|
||||
int checkCount = 0;
|
||||
Location lampLoc;
|
||||
|
||||
public WatchLamp()
|
||||
{
|
||||
if (!Settings.TerrainAndMovements)
|
||||
{
|
||||
LogToConsole("WARNING: Terrain handling is disabled in INI file.");
|
||||
LogToConsole("WARNING: This means this bot cannot watch for lamps.");
|
||||
UnloadBot();
|
||||
}
|
||||
else
|
||||
{
|
||||
lampLoc = new Location(lampX, lampY, lampZ);
|
||||
LogToConsole("Watching lamp at " + lampLoc);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (checkCount > 10)
|
||||
{
|
||||
checkCount = 0;
|
||||
Material blockType = GetWorld().GetBlock(lampLoc).Type;
|
||||
switch (blockType)
|
||||
{
|
||||
case Material.RedstoneLampOn:
|
||||
//Lamp is on. All right. Nothing to say here.
|
||||
break;
|
||||
case Material.RedstoneLampOff:
|
||||
LogToConsole("Lamp at " + lampLoc + " is currently turned OFF !!!");
|
||||
for (int i = 0; i < 3; i++)
|
||||
Console.Beep();
|
||||
break;
|
||||
default:
|
||||
LogToConsole("Block at " + lampLoc + " is not a lamp: " + blockType + "...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else checkCount++;
|
||||
}
|
||||
}
|
||||
14
MinecraftClient/config/sample-script.cs
Normal file
14
MinecraftClient/config/sample-script.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//MCCScript 1.0
|
||||
|
||||
/* This is a sample script for Minecraft Console Client
|
||||
* The code provided in this file will be compiled at runtime and executed
|
||||
* Allowed instructions: Any C# code AND methods provided by the MCC API */
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
int count = MCC.GetVarAsInt("test") + 1;
|
||||
MCC.SetVar("test", count);
|
||||
MCC.SendText("Hello World no. " + count);
|
||||
MCC.LogToConsole("Sleeping for 5 seconds...");
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue