Add basic location handling

- Retrieve player location from the server
- Send back player location from the server
- Requires that a specific setting is enabled
- Should allow items to be picked up by the player
- May also trigger some anti chead plugins
This commit is contained in:
ORelio 2015-11-27 17:16:33 +01:00
parent 5654871a57
commit 72bd485e67
11 changed files with 319 additions and 17 deletions

View file

@ -458,7 +458,7 @@ namespace MinecraftClient
protected void ReconnectToTheServer(int ExtraAttempts = 3) protected void ReconnectToTheServer(int ExtraAttempts = 3)
{ {
McTcpClient.AttemptsLeft = ExtraAttempts; McTcpClient.ReconnectionAttemptsLeft = ExtraAttempts;
Program.Restart(); Program.Restart();
} }

View file

@ -25,14 +25,14 @@ namespace MinecraftClient.ChatBots
{ {
attempts = retries; attempts = retries;
if (attempts == -1) { attempts = int.MaxValue; } if (attempts == -1) { attempts = int.MaxValue; }
McTcpClient.AttemptsLeft = attempts; McTcpClient.ReconnectionAttemptsLeft = attempts;
delay = DelayBeforeRelog; delay = DelayBeforeRelog;
if (delay < 1) { delay = 1; } if (delay < 1) { delay = 1; }
} }
public override void Initialize() public override void Initialize()
{ {
McTcpClient.AttemptsLeft = attempts; McTcpClient.ReconnectionAttemptsLeft = attempts;
if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile)) if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile))
{ {
dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile); dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile);
@ -55,7 +55,7 @@ namespace MinecraftClient.ChatBots
{ {
LogToConsole("Waiting " + delay + " seconds before reconnecting..."); LogToConsole("Waiting " + delay + " seconds before reconnecting...");
System.Threading.Thread.Sleep(delay * 1000); System.Threading.Thread.Sleep(delay * 1000);
McTcpClient.AttemptsLeft = attempts; McTcpClient.ReconnectionAttemptsLeft = attempts;
ReconnectToTheServer(); ReconnectToTheServer();
return true; return true;
} }

View file

@ -0,0 +1,159 @@
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>
/// 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 GetLongRepresentation()
{
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 FromLongRepresentation(ulong location)
{
return new Location(location >> 38, (location >> 26) & 0xFFF, location << 38 >> 38);
}
/// <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>
/// 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;
}
}
}

View file

@ -9,6 +9,7 @@ using System.Net;
using MinecraftClient.Protocol; using MinecraftClient.Protocol;
using MinecraftClient.Proxy; using MinecraftClient.Proxy;
using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Handlers.Forge;
using MinecraftClient.Mapping;
namespace MinecraftClient namespace MinecraftClient
{ {
@ -18,16 +19,20 @@ namespace MinecraftClient
public class McTcpClient : IMinecraftComHandler public class McTcpClient : IMinecraftComHandler
{ {
private static List<string> cmd_names = new List<string>(); public static int ReconnectionAttemptsLeft = 0;
private static Dictionary<string, Command> cmds = new Dictionary<string, Command>();
private List<ChatBot> bots = new List<ChatBot>(); 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 Dictionary<Guid, string> onlinePlayers = new Dictionary<Guid, string>();
private static List<ChatBots.Script> scripts_on_hold = new List<ChatBots.Script>();
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(); Settings.SingleCommand = ""; } public void BotLoad(ChatBot b) { b.SetHandler(this); bots.Add(b); b.Initialize(); Settings.SingleCommand = ""; }
public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); } public void BotUnLoad(ChatBot b) { bots.RemoveAll(item => object.ReferenceEquals(item, b)); }
public void BotClear() { bots.Clear(); } public void BotClear() { bots.Clear(); }
public static int AttemptsLeft = 0; private Location location;
private int updateTicks = 0;
private string host; private string host;
private int port; private int port;
@ -40,6 +45,7 @@ namespace MinecraftClient
public string GetUsername() { return username; } public string GetUsername() { return username; }
public string GetUserUUID() { return uuid; } public string GetUserUUID() { return uuid; }
public string GetSessionID() { return sessionid; } public string GetSessionID() { return sessionid; }
public Location GetCurrentLocation() { return location; }
TcpClient client; TcpClient client;
IMinecraftCom handler; IMinecraftCom handler;
@ -162,10 +168,10 @@ namespace MinecraftClient
if (retry) if (retry)
{ {
if (AttemptsLeft > 0) if (ReconnectionAttemptsLeft > 0)
{ {
ConsoleIO.WriteLogLine("Waiting 5 seconds (" + AttemptsLeft + " attempts left)..."); ConsoleIO.WriteLogLine("Waiting 5 seconds (" + ReconnectionAttemptsLeft + " attempts left)...");
Thread.Sleep(5000); AttemptsLeft--; Program.Restart(); Thread.Sleep(5000); ReconnectionAttemptsLeft--; Program.Restart();
} }
else if (!singlecommand && Settings.interactiveMode) else if (!singlecommand && Settings.interactiveMode)
{ {
@ -330,6 +336,34 @@ namespace MinecraftClient
handler.SendBrandInfo(Settings.BrandInfo.Trim()); handler.SendBrandInfo(Settings.BrandInfo.Trim());
} }
/// <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)
{
if (relative)
{
this.location += location;
}
else this.location = location;
}
/// <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> /// <summary>
/// Received some text from the server /// Received some text from the server
/// </summary> /// </summary>
@ -409,6 +443,16 @@ namespace MinecraftClient
else throw; //ThreadAbortException should not be caught else throw; //ThreadAbortException should not be caught
} }
} }
if (Settings.TerrainAndMovements)
{
if (updateTicks >= 10)
{
handler.SendLocationUpdate(location, true); //TODO handle onGround once terrain data is available
updateTicks = 0;
}
updateTicks++;
}
} }
/// <summary> /// <summary>

View file

@ -152,6 +152,7 @@
<Compile Include="Proxy\Handlers\Utils.cs" /> <Compile Include="Proxy\Handlers\Utils.cs" />
<Compile Include="Settings.cs" /> <Compile Include="Settings.cs" />
<Compile Include="Commands\List.cs" /> <Compile Include="Commands\List.cs" />
<Compile Include="Mapping\Location.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client"> <BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">

View file

@ -19,7 +19,11 @@ namespace MinecraftClient
{ {
private static McTcpClient Client; private static McTcpClient Client;
public static string[] startupargs; public static string[] startupargs;
public const string Version = "1.8.2"; public const string Version = "1.8.2";
public const string MCLowestVersion = "1.4.6";
public const string MCHighestVersion = "1.8.8";
private static Thread offlinePrompt = null; private static Thread offlinePrompt = null;
private static bool useMcVersionOnce = false; private static bool useMcVersionOnce = false;
@ -29,7 +33,7 @@ namespace MinecraftClient
static void Main(string[] args) static void Main(string[] args)
{ {
Console.WriteLine("Console Client for MC 1.4.6 to 1.8.8 - v" + Version + " - By ORelio & Contributors"); Console.WriteLine("Console Client for MC {0} to {1} - v{2} - By ORelio & Contributors", MCLowestVersion, MCHighestVersion, Version);
//Basic Input/Output ? //Basic Input/Output ?
if (args.Length >= 1 && args[args.Length - 1] == "BasicIO") if (args.Length >= 1 && args[args.Length - 1] == "BasicIO")
@ -137,6 +141,8 @@ namespace MinecraftClient
ConsoleIcon.setPlayerIconAsync(Settings.Username); ConsoleIcon.setPlayerIconAsync(Settings.Username);
Console.WriteLine("Success. (session ID: " + sessionID + ')'); Console.WriteLine("Success. (session ID: " + sessionID + ')');
//ProtocolHandler.RealmsListWorlds(Settings.Username, UUID, sessionID); //TODO REMOVE
if (Settings.ServerIP == "") if (Settings.ServerIP == "")
{ {

View file

@ -7,6 +7,7 @@ using System.Threading;
using MinecraftClient.Crypto; using MinecraftClient.Crypto;
using MinecraftClient.Proxy; using MinecraftClient.Proxy;
using System.Security.Cryptography; using System.Security.Cryptography;
using MinecraftClient.Mapping;
namespace MinecraftClient.Protocol.Handlers namespace MinecraftClient.Protocol.Handlers
{ {
@ -615,6 +616,11 @@ namespace MinecraftClient.Protocol.Handlers
return false; //Only supported since MC 1.7 return false; //Only supported since MC 1.7
} }
public bool SendLocationUpdate(Location location, bool onGround)
{
return false; //Currently not implemented
}
/// <summary> /// <summary>
/// Send a plugin channel packet to the server. /// Send a plugin channel packet to the server.
/// </summary> /// </summary>

View file

@ -8,6 +8,7 @@ using MinecraftClient.Crypto;
using MinecraftClient.Proxy; using MinecraftClient.Proxy;
using System.Security.Cryptography; using System.Security.Cryptography;
using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Handlers.Forge;
using MinecraftClient.Mapping;
namespace MinecraftClient.Protocol.Handlers namespace MinecraftClient.Protocol.Handlers
{ {
@ -162,6 +163,23 @@ namespace MinecraftClient.Protocol.Handlers
catch (IndexOutOfRangeException) { /* No message type */ } catch (IndexOutOfRangeException) { /* No message type */ }
handler.OnTextReceived(ChatParser.ParseText(message)); handler.OnTextReceived(ChatParser.ParseText(message));
break; break;
case 0x08:
if (Settings.TerrainAndMovements)
{
double x = readNextDouble(ref packetData);
double y = readNextDouble(ref packetData);
double z = readNextDouble(ref packetData);
byte locMask = readNextByte(ref packetData);
Location location = handler.GetCurrentLocation();
location.X = (locMask & 1 << 0) != 0 ? location.X + x : x;
location.Y = (locMask & 1 << 1) != 0 ? location.Y + y : y;
location.Z = (locMask & 1 << 2) != 0 ? location.Z + z : z;
handler.UpdateLocation(location);
}
break;
case 0x38: //Player List update case 0x38: //Player List update
if (protocolversion >= MC18Version) if (protocolversion >= MC18Version)
{ {
@ -509,6 +527,18 @@ namespace MinecraftClient.Protocol.Handlers
return readData(len, ref cache); return readData(len, ref cache);
} }
/// <summary>
/// Read a double from a cache of bytes and remove it from the cache
/// </summary>
/// <returns>The double value</returns>
private static double readNextDouble(ref byte[] cache)
{
byte[] rawValue = readData(8, ref cache);
Array.Reverse(rawValue); //Endianness
return BitConverter.ToDouble(rawValue, 0);
}
/// <summary> /// <summary>
/// Read an integer from the network /// Read an integer from the network
/// </summary> /// </summary>
@ -602,6 +632,19 @@ namespace MinecraftClient.Protocol.Handlers
return bytes.ToArray(); return bytes.ToArray();
} }
/// <summary>
/// Get byte array representing a double
/// </summary>
/// <param name="array">Array to process</param>
/// <returns>Array ready to send</returns>
private byte[] getDouble(double number)
{
byte[] theDouble = BitConverter.GetBytes(number);
Array.Reverse(theDouble); //Endianness
return theDouble;
}
/// <summary> /// <summary>
/// Get byte array with length information prepended to it /// Get byte array with length information prepended to it
/// </summary> /// </summary>
@ -931,6 +974,29 @@ namespace MinecraftClient.Protocol.Handlers
return SendPluginChannelPacket("MC|Brand", getString(brandInfo)); return SendPluginChannelPacket("MC|Brand", getString(brandInfo));
} }
/// <summary>
/// Send a location update to the server
/// </summary>
/// <param name="location">The new location of the player</param>
/// <param name="onGround">True if the player is on the ground</param>
/// <returns>True if the location update was successfully sent</returns>
public bool SendLocationUpdate(Location location, bool onGround)
{
if (Settings.TerrainAndMovements)
{
try
{
SendPacket(0x04, concatBytes(
getDouble(location.X), getDouble(location.X), getDouble(location.X),
new byte[] { onGround ? (byte)1 : (byte)0 }));
return true;
}
catch (SocketException) { return false; }
}
else return false;
}
/// <summary> /// <summary>
/// Send a plugin channel packet (0x17) to the server, compression and encryption will be handled automatically /// Send a plugin channel packet (0x17) to the server, compression and encryption will be handled automatically
/// </summary> /// </summary>

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MinecraftClient.Crypto; using MinecraftClient.Crypto;
using MinecraftClient.Mapping;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
@ -45,7 +46,7 @@ namespace MinecraftClient.Protocol
bool SendRespawnPacket(); bool SendRespawnPacket();
/// <summary> /// <summary>
/// Tell the server what client is being used to connect to the server /// Inform the server of the client being used to connect
/// </summary> /// </summary>
/// <param name="brandInfo">Client string describing the client</param> /// <param name="brandInfo">Client string describing the client</param>
/// <returns>True if brand info was successfully sent</returns> /// <returns>True if brand info was successfully sent</returns>
@ -53,10 +54,17 @@ namespace MinecraftClient.Protocol
bool SendBrandInfo(string brandInfo); bool SendBrandInfo(string brandInfo);
/// <summary> /// <summary>
/// Send a plugin channel packet to the server. /// Send a location update telling that we moved to that location
///
/// http://dinnerbone.com/blog/2012/01/13/minecraft-plugin-channels-messaging/
/// </summary> /// </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="channel">Channel to send packet on</param>
/// <param name="data">packet Data</param> /// <param name="data">packet Data</param>
/// <returns>True if message was successfully sent</returns> /// <returns>True if message was successfully sent</returns>

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using MinecraftClient.Mapping;
namespace MinecraftClient.Protocol namespace MinecraftClient.Protocol
{ {
@ -22,6 +23,7 @@ namespace MinecraftClient.Protocol
string GetUserUUID(); string GetUserUUID();
string GetSessionID(); string GetSessionID();
string[] GetOnlinePlayers(); string[] GetOnlinePlayers();
Location GetCurrentLocation();
/// <summary> /// <summary>
/// Called when a server was successfully joined /// Called when a server was successfully joined
@ -50,6 +52,13 @@ namespace MinecraftClient.Protocol
void OnPlayerLeave(Guid uuid); 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> /// <summary>
/// This method is called when the connection has been lost /// This method is called when the connection has been lost
/// </summary> /// </summary>

View file

@ -54,6 +54,7 @@ namespace MinecraftClient
public static string BrandInfo = MCCBrandInfo; public static string BrandInfo = MCCBrandInfo;
public static bool DisplaySystemMessages = true; public static bool DisplaySystemMessages = true;
public static bool DisplayXPBarMessages = true; public static bool DisplayXPBarMessages = true;
public static bool TerrainAndMovements = false;
//AntiAFK Settings //AntiAFK Settings
public static bool AntiAFK_Enabled = false; public static bool AntiAFK_Enabled = false;
@ -178,6 +179,7 @@ namespace MinecraftClient
case "scriptcache": CacheScripts = str2bool(argValue); break; case "scriptcache": CacheScripts = str2bool(argValue); break;
case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break; case "showsystemmessages": DisplaySystemMessages = str2bool(argValue); break;
case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break; case "showxpbarmessages": DisplayXPBarMessages = str2bool(argValue); break;
case "handleterrainandmovements": TerrainAndMovements = str2bool(argValue); break;
case "botowners": case "botowners":
Bots_Owners.Clear(); Bots_Owners.Clear();
@ -407,6 +409,7 @@ namespace MinecraftClient
+ "chatbotlogfile= #leave empty for no logfile\r\n" + "chatbotlogfile= #leave empty for no logfile\r\n"
+ "showsystemmessages=true #system messages for server ops\r\n" + "showsystemmessages=true #system messages for server ops\r\n"
+ "showxpbarmessages=true #messages displayed above xp bar\r\n" + "showxpbarmessages=true #messages displayed above xp bar\r\n"
+ "handleterrainandmovements=false #requires more ram and cpu\r\n"
+ "accountlist=accounts.txt\r\n" + "accountlist=accounts.txt\r\n"
+ "serverlist=servers.txt\r\n" + "serverlist=servers.txt\r\n"
+ "playerheadicon=true\r\n" + "playerheadicon=true\r\n"