2013-07-18 09:27:19 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Net.Sockets ;
using System.Threading ;
using System.IO ;
using System.Net ;
2014-05-31 01:59:03 +02:00
using MinecraftClient.Protocol ;
using MinecraftClient.Proxy ;
2015-10-23 16:54:36 -07:00
using MinecraftClient.Protocol.Handlers.Forge ;
2015-11-27 17:16:33 +01:00
using MinecraftClient.Mapping ;
2013-07-18 09:27:19 +02:00
namespace MinecraftClient
{
/// <summary>
/// The main client class, used to connect to a Minecraft server.
/// </summary>
2014-05-31 01:59:03 +02:00
public class McTcpClient : IMinecraftComHandler
2013-07-18 09:27:19 +02:00
{
2015-11-27 17:16:33 +01:00
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 > ( ) ;
2016-08-27 15:46:34 +02:00
private readonly Dictionary < Guid , string > onlinePlayers = new Dictionary < Guid , string > ( ) ;
2015-11-27 17:16:33 +01:00
private readonly List < ChatBot > bots = new List < ChatBot > ( ) ;
2017-03-29 21:25:14 +02:00
private static readonly List < ChatBot > botsOnHold = new List < ChatBot > ( ) ;
2014-05-31 01:59:03 +02:00
2016-02-07 14:24:01 -08:00
private readonly Dictionary < string , List < ChatBot > > registeredBotPluginChannels = new Dictionary < string , List < ChatBot > > ( ) ;
private readonly List < string > registeredServerPluginChannels = new List < String > ( ) ;
2015-11-30 15:30:49 +01:00
private object locationLock = new object ( ) ;
2015-12-13 21:58:55 +01:00
private bool locationReceived = false ;
2015-11-30 15:30:49 +01:00
private World world = new World ( ) ;
2015-12-12 16:48:29 +01:00
private Queue < Location > steps ;
private Queue < Location > path ;
2015-11-27 17:16:33 +01:00
private Location location ;
2017-03-10 23:40:02 +01:00
private byte [ ] yawpitch ;
private double motionY ;
2013-07-18 09:27:19 +02:00
2014-05-31 01:59:03 +02:00
private string host ;
private int port ;
private string username ;
private string uuid ;
private string sessionid ;
2015-06-20 22:58:18 +02:00
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 ; }
2015-11-27 17:16:33 +01:00
public Location GetCurrentLocation ( ) { return location ; }
2015-11-30 15:30:49 +01:00
public World GetWorld ( ) { return world ; }
2015-05-19 15:36:20 +01:00
2013-07-18 09:27:19 +02:00
TcpClient client ;
2014-05-31 01:59:03 +02:00
IMinecraftCom handler ;
2014-06-19 19:24:03 +02:00
Thread cmdprompt ;
2013-07-18 09:27:19 +02:00
/// <summary>
2014-05-31 01:59:03 +02:00
/// Starts the main chat client
2013-07-18 09:27:19 +02:00
/// </summary>
/// <param name="username">The chosen username of a premium Minecraft Account</param>
2014-06-13 16:50:55 +02:00
/// <param name="uuid">The player's UUID for online-mode authentication</param>
/// <param name="sessionID">A valid sessionID obtained after logging in</param>
/// <param name="server_ip">The server IP</param>
/// <param name="port">The server port to use</param>
/// <param name="protocolversion">Minecraft protocol version to use</param>
2015-10-23 16:54:36 -07:00
public McTcpClient ( string username , string uuid , string sessionID , int protocolversion , ForgeInfo forgeInfo , string server_ip , ushort port )
2013-07-18 09:27:19 +02:00
{
2015-10-23 16:54:36 -07:00
StartClient ( username , uuid , sessionID , server_ip , port , protocolversion , forgeInfo , false , "" ) ;
2013-07-18 09:27:19 +02:00
}
/// <summary>
2014-05-31 01:59:03 +02:00
/// Starts the main chat client in single command sending mode
2013-07-18 09:27:19 +02:00
/// </summary>
/// <param name="username">The chosen username of a premium Minecraft Account</param>
2014-06-13 16:50:55 +02:00
/// <param name="uuid">The player's UUID for online-mode authentication</param>
/// <param name="sessionID">A valid sessionID obtained after logging in</param>
/// <param name="server_ip">The server IP</param>
/// <param name="port">The server port to use</param>
/// <param name="protocolversion">Minecraft protocol version to use</param>
2013-07-18 09:27:19 +02:00
/// <param name="command">The text or command to send.</param>
2015-10-23 16:54:36 -07:00
public McTcpClient ( string username , string uuid , string sessionID , string server_ip , ushort port , int protocolversion , ForgeInfo forgeInfo , string command )
2013-07-18 09:27:19 +02:00
{
2015-10-23 16:54:36 -07:00
StartClient ( username , uuid , sessionID , server_ip , port , protocolversion , forgeInfo , true , command ) ;
2013-07-18 09:27:19 +02:00
}
/// <summary>
/// Starts the main chat client, wich will login to the server using the MinecraftCom class.
/// </summary>
/// <param name="user">The chosen username of a premium Minecraft Account</param>
/// <param name="sessionID">A valid sessionID obtained with MinecraftCom.GetLogin()</param>
2014-06-13 16:50:55 +02:00
/// <param name="server_ip">The server IP</param>
/// <param name="port">The server port to use</param>
/// <param name="protocolversion">Minecraft protocol version to use</param>
/// <param name="uuid">The player's UUID for online-mode authentication</param>
2013-07-18 09:27:19 +02:00
/// <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>
2015-10-23 16:54:36 -07:00
private void StartClient ( string user , string uuid , string sessionID , string server_ip , ushort port , int protocolversion , ForgeInfo forgeInfo , bool singlecommand , string command )
2013-07-18 09:27:19 +02:00
{
2015-04-06 11:42:43 +02:00
bool retry = false ;
2014-05-31 01:59:03 +02:00
this . sessionid = sessionID ;
this . uuid = uuid ;
this . username = user ;
2014-06-13 16:50:55 +02:00
this . host = server_ip ;
this . port = port ;
2013-07-18 09:27:19 +02:00
2014-05-31 01:59:03 +02:00
if ( ! singlecommand )
{
2017-03-29 21:25:14 +02:00
if ( botsOnHold . Count = = 0 )
{
if ( Settings . AntiAFK_Enabled ) { BotLoad ( new ChatBots . AntiAFK ( Settings . AntiAFK_Delay ) ) ; }
if ( Settings . Hangman_Enabled ) { BotLoad ( new ChatBots . HangmanGame ( Settings . Hangman_English ) ) ; }
if ( Settings . Alerts_Enabled ) { BotLoad ( new ChatBots . Alerts ( ) ) ; }
if ( Settings . ChatLog_Enabled ) { BotLoad ( new ChatBots . ChatLog ( Settings . ExpandVars ( Settings . ChatLog_File ) , Settings . ChatLog_Filter , Settings . ChatLog_DateTime ) ) ; }
if ( Settings . PlayerLog_Enabled ) { BotLoad ( new ChatBots . PlayerListLogger ( Settings . PlayerLog_Delay , Settings . ExpandVars ( Settings . PlayerLog_File ) ) ) ; }
if ( Settings . AutoRelog_Enabled ) { BotLoad ( new ChatBots . AutoRelog ( Settings . AutoRelog_Delay , Settings . AutoRelog_Retries ) ) ; }
if ( Settings . ScriptScheduler_Enabled ) { BotLoad ( new ChatBots . ScriptScheduler ( Settings . ExpandVars ( Settings . ScriptScheduler_TasksFile ) ) ) ; }
if ( Settings . RemoteCtrl_Enabled ) { BotLoad ( new ChatBots . RemoteControl ( ) ) ; }
if ( Settings . AutoRespond_Enabled ) { BotLoad ( new ChatBots . AutoRespond ( Settings . AutoRespond_Matches ) ) ; }
//Add your ChatBot here by uncommenting and adapting
//BotLoad(new ChatBots.YourBot());
}
2014-05-31 01:59:03 +02:00
}
2013-07-18 09:27:19 +02:00
try
{
2015-10-22 20:56:08 +02:00
client = ProxyHandler . newTcpClient ( host , port ) ;
2013-07-18 09:27:19 +02:00
client . ReceiveBufferSize = 1024 * 1024 ;
2016-10-06 19:13:58 +02:00
handler = Protocol . ProtocolHandler . GetProtocolHandler ( client , protocolversion , forgeInfo , this ) ;
2014-05-31 12:56:54 +02:00
Console . WriteLine ( "Version is supported.\nLogging in..." ) ;
2015-04-06 11:42:43 +02:00
try
2013-07-18 09:27:19 +02:00
{
2015-04-06 11:42:43 +02:00
if ( handler . Login ( ) )
2013-07-18 09:27:19 +02:00
{
2015-04-06 11:42:43 +02:00
if ( singlecommand )
{
handler . SendChatMessage ( command ) ;
ConsoleIO . WriteLineFormatted ( "§7Command §8" + command + "§7 sent." ) ;
Thread . Sleep ( 5000 ) ;
handler . Disconnect ( ) ;
Thread . Sleep ( 1000 ) ;
}
else
{
2017-03-29 21:25:14 +02:00
foreach ( ChatBot bot in botsOnHold )
BotLoad ( bot , false ) ;
botsOnHold . Clear ( ) ;
2015-04-06 11:42:43 +02:00
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 ( ) ;
}
2013-07-18 09:27:19 +02:00
}
}
2015-04-06 11:42:43 +02:00
catch ( Exception e )
{
ConsoleIO . WriteLineFormatted ( "§8" + e . Message ) ;
Console . WriteLine ( "Failed to join this server." ) ;
retry = true ;
}
2013-07-18 09:27:19 +02:00
}
2015-04-20 17:26:16 +02:00
catch ( SocketException e )
2013-07-18 09:27:19 +02:00
{
2015-04-20 17:26:16 +02:00
ConsoleIO . WriteLineFormatted ( "§8" + e . Message ) ;
2013-07-18 09:27:19 +02:00
Console . WriteLine ( "Failed to connect to this IP." ) ;
2015-04-06 11:42:43 +02:00
retry = true ;
}
if ( retry )
{
2015-11-27 17:16:33 +01:00
if ( ReconnectionAttemptsLeft > 0 )
2013-07-18 09:27:19 +02:00
{
2015-11-27 17:16:33 +01:00
ConsoleIO . WriteLogLine ( "Waiting 5 seconds (" + ReconnectionAttemptsLeft + " attempts left)..." ) ;
Thread . Sleep ( 5000 ) ; ReconnectionAttemptsLeft - - ; Program . Restart ( ) ;
2013-07-18 09:27:19 +02:00
}
2015-03-25 22:50:20 +01:00
else if ( ! singlecommand & & Settings . interactiveMode )
{
2015-04-22 10:27:53 +02:00
Program . HandleFailure ( ) ;
2015-03-25 22:50:20 +01:00
}
2013-07-18 09:27:19 +02:00
}
}
/// <summary>
/// Allows the user to send chat messages, commands, and to leave the server.
/// </summary>
2014-06-19 19:24:03 +02:00
private void CommandPrompt ( )
2013-07-18 09:27:19 +02:00
{
try
{
2014-05-31 01:59:03 +02:00
string text = "" ;
Thread . Sleep ( 500 ) ;
2014-01-12 13:38:52 +01:00
handler . SendRespawnPacket ( ) ;
2013-07-18 09:27:19 +02:00
while ( client . Client . Connected )
{
text = ConsoleIO . ReadLine ( ) ;
2013-08-18 18:26:20 +02:00
if ( ConsoleIO . basicIO & & text . Length > 0 & & text [ 0 ] = = ( char ) 0x00 )
2013-07-18 09:27:19 +02:00
{
2013-08-18 18:26:20 +02:00
//Process a request from the GUI
string [ ] command = text . Substring ( 1 ) . Split ( ( char ) 0x00 ) ;
switch ( command [ 0 ] . ToLower ( ) )
2013-07-18 09:27:19 +02:00
{
2013-08-18 18:26:20 +02:00
case "autocomplete" :
if ( command . Length > 1 ) { ConsoleIO . WriteLine ( ( char ) 0x00 + "autocomplete" + ( char ) 0x00 + handler . AutoComplete ( command [ 1 ] ) ) ; }
else Console . WriteLine ( ( char ) 0x00 + "autocomplete" + ( char ) 0x00 ) ;
break ;
}
}
else
{
2014-01-12 13:38:52 +01:00
text = text . Trim ( ) ;
2014-06-14 13:20:15 +02:00
if ( text . Length > 0 )
2014-01-12 13:38:52 +01:00
{
2014-06-18 00:49:45 +02:00
if ( Settings . internalCmdChar = = ' ' | | text [ 0 ] = = Settings . internalCmdChar )
2013-07-18 09:27:19 +02:00
{
2014-06-14 18:48:43 +02:00
string response_msg = "" ;
2014-06-18 00:49:45 +02:00
string command = Settings . internalCmdChar = = ' ' ? text : text . Substring ( 1 ) ;
2015-06-20 22:58:18 +02:00
if ( ! PerformInternalCommand ( Settings . ExpandVars ( command ) , ref response_msg ) & & Settings . internalCmdChar = = '/' )
2013-07-18 09:27:19 +02:00
{
2014-06-19 19:24:03 +02:00
SendText ( text ) ;
2014-06-14 18:48:43 +02:00
}
else if ( response_msg . Length > 0 )
{
2014-06-18 00:49:45 +02:00
ConsoleIO . WriteLineFormatted ( "§8MCC: " + response_msg ) ;
2013-07-18 09:27:19 +02:00
}
}
2014-06-19 19:24:03 +02:00
else SendText ( text ) ;
2013-07-18 09:27:19 +02:00
}
}
}
}
catch ( IOException ) { }
2015-07-30 17:32:42 +02:00
catch ( NullReferenceException ) { }
2013-07-18 09:27:19 +02:00
}
2014-06-14 13:20:15 +02:00
/// <summary>
2014-06-19 19:24:03 +02:00
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
2014-06-14 13:20:15 +02:00
/// </summary>
/// <param name="command">The command</param>
/// <param name="interactive_mode">Set to true if command was sent by the user using the command prompt</param>
2014-06-14 18:48:43 +02:00
/// <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>
2015-06-20 22:58:18 +02:00
public bool PerformInternalCommand ( string command , ref string response_msg )
2014-06-14 13:20:15 +02:00
{
2014-06-18 13:32:17 +02:00
/* Load commands from the 'Commands' namespace */
2014-06-14 13:20:15 +02:00
2014-06-18 13:32:17 +02:00
if ( cmds . Count = = 0 )
{
Type [ ] cmds_classes = Program . GetTypesInNamespace ( "MinecraftClient.Commands" ) ;
foreach ( Type type in cmds_classes )
{
if ( type . IsSubclassOf ( typeof ( Command ) ) )
2014-06-14 18:48:43 +02:00
{
2014-06-18 13:32:17 +02:00
try
2014-06-14 18:48:43 +02:00
{
2014-06-18 13:32:17 +02:00
Command cmd = ( Command ) Activator . CreateInstance ( type ) ;
cmds [ cmd . CMDName . ToLower ( ) ] = cmd ;
cmd_names . Add ( cmd . CMDName . ToLower ( ) ) ;
foreach ( string alias in cmd . getCMDAliases ( ) )
cmds [ alias . ToLower ( ) ] = cmd ;
}
catch ( Exception e )
{
ConsoleIO . WriteLine ( e . Message ) ;
2014-06-14 18:48:43 +02:00
}
}
2014-06-18 13:32:17 +02:00
}
}
2014-06-14 13:20:15 +02:00
2014-06-18 13:32:17 +02:00
/* Process the provided command */
2014-06-14 13:20:15 +02:00
2014-06-18 13:32:17 +02:00
string command_name = command . Split ( ' ' ) [ 0 ] . ToLower ( ) ;
if ( command_name = = "help" )
{
if ( Command . hasArg ( command ) )
{
string help_cmdname = Command . getArgs ( command ) [ 0 ] . ToLower ( ) ;
if ( help_cmdname = = "help" )
2014-06-14 18:48:43 +02:00
{
2014-06-18 13:32:17 +02:00
response_msg = "help <cmdname>: show brief help about a command." ;
2014-06-14 18:48:43 +02:00
}
2014-06-18 13:32:17 +02:00
else if ( cmds . ContainsKey ( help_cmdname ) )
2014-06-14 18:48:43 +02:00
{
2014-06-18 13:32:17 +02:00
response_msg = cmds [ help_cmdname ] . CMDDesc ;
2014-06-14 18:48:43 +02:00
}
2014-06-18 13:32:17 +02:00
else response_msg = "Unknown command '" + command_name + "'. Use 'help' for command list." ;
}
2017-03-09 21:13:52 +01:00
else response_msg = "help <cmdname>. Available commands: " + String . Join ( ", " , cmd_names . ToArray ( ) ) + ". For server help, use '" + Settings . internalCmdChar + "send /help' instead." ;
2014-06-18 13:32:17 +02:00
}
else if ( cmds . ContainsKey ( command_name ) )
{
response_msg = cmds [ command_name ] . Run ( this , command ) ;
}
else
{
2014-08-18 15:10:15 +02:00
response_msg = "Unknown command '" + command_name + "'. Use '" + ( Settings . internalCmdChar = = ' ' ? "" : "" + Settings . internalCmdChar ) + "help' for help." ;
2014-06-18 13:32:17 +02:00
return false ;
2014-06-14 13:20:15 +02:00
}
return true ;
}
2013-07-18 09:27:19 +02:00
/// <summary>
2014-05-31 01:59:03 +02:00
/// Disconnect the client from the server
2013-07-18 09:27:19 +02:00
/// </summary>
2014-05-31 01:59:03 +02:00
public void Disconnect ( )
2013-07-18 09:27:19 +02:00
{
2017-03-29 21:25:14 +02:00
botsOnHold . Clear ( ) ;
botsOnHold . AddRange ( bots ) ;
2014-05-31 01:59:03 +02:00
2015-03-02 21:35:45 +01:00
if ( handler ! = null )
{
handler . Disconnect ( ) ;
handler . Dispose ( ) ;
}
2014-06-19 19:24:03 +02:00
if ( cmdprompt ! = null )
cmdprompt . Abort ( ) ;
2014-05-31 01:59:03 +02:00
Thread . Sleep ( 1000 ) ;
2015-06-20 22:58:18 +02:00
if ( client ! = null )
client . Close ( ) ;
2014-05-31 01:59:03 +02:00
}
2013-07-18 09:27:19 +02:00
2015-09-30 20:01:57 +02:00
/// <summary>
2016-08-22 19:09:43 +02:00
/// Load a new bot
/// </summary>
2017-03-29 21:25:14 +02:00
public void BotLoad ( ChatBot b , bool init = true )
2016-08-22 19:09:43 +02:00
{
b . SetHandler ( this ) ;
bots . Add ( b ) ;
2017-03-29 21:25:14 +02:00
if ( init )
b . Initialize ( ) ;
2016-08-22 19:09:43 +02:00
if ( this . handler ! = null )
b . AfterGameJoined ( ) ;
Settings . SingleCommand = "" ;
}
/// <summary>
/// Unload a bot
/// </summary>
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 ) ;
}
}
/// <summary>
/// Clear bots
2015-09-30 20:01:57 +02:00
/// </summary>
2016-08-22 19:09:43 +02:00
public void BotClear ( )
{
bots . Clear ( ) ;
}
2015-09-30 20:01:57 +02:00
2016-08-22 19:09:43 +02:00
/// <summary>
/// Called when a server was successfully joined
/// </summary>
2015-09-30 20:01:57 +02:00
public void OnGameJoined ( )
{
if ( ! String . IsNullOrWhiteSpace ( Settings . BrandInfo ) )
handler . SendBrandInfo ( Settings . BrandInfo . Trim ( ) ) ;
2016-08-26 12:19:25 +02:00
if ( Settings . MCSettings_Enabled )
handler . SendClientSettings (
Settings . MCSettings_Locale ,
Settings . MCSettings_RenderDistance ,
Settings . MCSettings_Difficulty ,
Settings . MCSettings_ChatMode ,
Settings . MCSettings_ChatColors ,
Settings . MCSettings_Skin_All ,
Settings . MCSettings_MainHand ) ;
2016-02-07 14:47:03 -08:00
foreach ( ChatBot bot in bots )
bot . AfterGameJoined ( ) ;
2015-09-30 20:01:57 +02:00
}
2015-11-27 17:16:33 +01:00
/// <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 )
{
2015-11-30 15:30:49 +01:00
lock ( locationLock )
2015-11-27 17:16:33 +01:00
{
2015-11-30 15:30:49 +01:00
if ( relative )
{
this . location + = location ;
}
else this . location = location ;
2015-12-13 21:58:55 +01:00
locationReceived = true ;
2015-11-27 17:16:33 +01:00
}
}
/// <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>
2017-03-10 23:40:02 +01:00
public void UpdateLocation ( Location location , byte [ ] yawpitch )
2015-11-27 17:16:33 +01:00
{
2017-03-10 23:40:02 +01:00
this . yawpitch = yawpitch ;
2015-11-27 17:16:33 +01:00
UpdateLocation ( location , false ) ;
}
2015-12-12 16:48:29 +01:00
/// <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 ;
}
}
2014-05-31 01:59:03 +02:00
/// <summary>
/// Received some text from the server
/// </summary>
/// <param name="text">Text received</param>
2016-10-07 19:52:28 +02:00
/// <param name="links">Links embedded in text</param>
public void OnTextReceived ( string text , IEnumerable < string > links )
2014-05-31 01:59:03 +02:00
{
ConsoleIO . WriteLineFormatted ( text , false ) ;
2016-10-07 19:52:28 +02:00
if ( Settings . DisplayChatLinks )
foreach ( string link in links )
ConsoleIO . WriteLineFormatted ( "§8MCC: Link: " + link , false ) ;
2015-03-23 13:57:31 +01:00
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
}
}
2014-05-31 01:59:03 +02:00
}
/// <summary>
/// When connection has been lost
/// </summary>
public void OnConnectionLost ( ChatBot . DisconnectReason reason , string message )
{
bool will_restart = false ;
switch ( reason )
2013-07-18 09:27:19 +02:00
{
2014-05-31 01:59:03 +02:00
case ChatBot . DisconnectReason . ConnectionLost :
message = "Connection has been lost." ;
ConsoleIO . WriteLine ( message ) ;
break ;
case ChatBot . DisconnectReason . InGameKick :
ConsoleIO . WriteLine ( "Disconnected by Server :" ) ;
2014-06-11 20:40:25 +02:00
ConsoleIO . WriteLineFormatted ( message ) ;
2014-05-31 01:59:03 +02:00
break ;
case ChatBot . DisconnectReason . LoginRejected :
ConsoleIO . WriteLine ( "Login failed :" ) ;
2014-06-11 20:40:25 +02:00
ConsoleIO . WriteLineFormatted ( message ) ;
2014-05-31 01:59:03 +02:00
break ;
2013-07-18 09:27:19 +02:00
}
2014-05-31 01:59:03 +02:00
foreach ( ChatBot bot in bots )
will_restart | = bot . OnDisconnect ( reason , message ) ;
2015-04-20 17:26:16 +02:00
if ( ! will_restart )
2015-04-22 10:27:53 +02:00
Program . HandleFailure ( ) ;
2013-07-18 09:27:19 +02:00
}
/// <summary>
2014-05-31 01:59:03 +02:00
/// Called ~10 times per second by the protocol handler
2013-07-18 09:27:19 +02:00
/// </summary>
2014-05-31 01:59:03 +02:00
public void OnUpdate ( )
2013-07-18 09:27:19 +02:00
{
2016-02-23 11:19:14 -07:00
foreach ( var bot in bots . ToArray ( ) )
2014-06-11 20:40:25 +02:00
{
try
{
2016-02-23 11:19:14 -07:00
bot . Update ( ) ;
bot . ProcessQueuedText ( ) ;
2014-06-11 20:40:25 +02:00
}
catch ( Exception e )
{
2014-09-07 15:11:39 +02:00
if ( ! ( e is ThreadAbortException ) )
{
2016-02-23 11:19:14 -07:00
ConsoleIO . WriteLineFormatted ( "§8Update: Got error from " + bot . ToString ( ) + ": " + e . ToString ( ) ) ;
2014-09-07 15:11:39 +02:00
}
else throw ; //ThreadAbortException should not be caught
2014-06-11 20:40:25 +02:00
}
}
2015-11-27 17:16:33 +01:00
2015-12-13 21:58:55 +01:00
if ( Settings . TerrainAndMovements & & locationReceived )
2015-11-27 17:16:33 +01:00
{
2015-12-09 23:04:00 +01:00
lock ( locationLock )
2015-11-27 17:16:33 +01:00
{
2015-12-12 16:48:29 +01:00
for ( int i = 0 ; i < 2 ; i + + ) //Needs to run at 20 tps; MCC runs at 10 tps
{
2017-03-10 23:40:02 +01:00
if ( yawpitch = = null )
{
if ( steps ! = null & & steps . Count > 0 )
location = steps . Dequeue ( ) ;
else if ( path ! = null & & path . Count > 0 )
steps = Movement . Move2Steps ( location , path . Dequeue ( ) , ref motionY ) ;
else location = Movement . HandleGravity ( world , location , ref motionY ) ;
}
handler . SendLocationUpdate ( location , Movement . IsOnGround ( world , location ) , yawpitch ) ;
2015-12-12 16:48:29 +01:00
}
2017-03-10 23:40:02 +01:00
yawpitch = null ; //First 2 updates must be player position AND look, and player must not move (to conform with vanilla)
2015-11-27 17:16:33 +01:00
}
}
2014-05-31 01:59:03 +02:00
}
/// <summary>
2014-06-14 13:20:15 +02:00
/// Send a chat message or command to the server
2014-05-31 01:59:03 +02:00
/// </summary>
2014-06-14 13:20:15 +02:00
/// <param name="text">Text to send to the server</param>
/// <returns>True if the text was sent with no error</returns>
2014-06-19 19:24:03 +02:00
public bool SendText ( string text )
2014-05-31 01:59:03 +02:00
{
2016-11-19 16:06:08 +01:00
int maxLength = handler . GetMaxChatMessageLength ( ) ;
if ( text . Length > maxLength ) //Message is too long?
2014-06-14 13:20:15 +02:00
{
if ( text [ 0 ] = = '/' )
{
2016-11-19 16:06:08 +01:00
//Send the first 100/256 chars of the command
text = text . Substring ( 0 , maxLength ) ;
2014-06-14 13:20:15 +02:00
return handler . SendChatMessage ( text ) ;
}
else
{
//Send the message splitted into several messages
2016-11-19 16:06:08 +01:00
while ( text . Length > maxLength )
2014-06-14 13:20:15 +02:00
{
2016-11-19 16:06:08 +01:00
handler . SendChatMessage ( text . Substring ( 0 , maxLength ) ) ;
text = text . Substring ( maxLength , text . Length - maxLength ) ;
2015-05-26 19:00:37 +02:00
if ( Settings . splitMessageDelay . TotalSeconds > 0 )
Thread . Sleep ( Settings . splitMessageDelay ) ;
2014-06-14 13:20:15 +02:00
}
return handler . SendChatMessage ( text ) ;
}
}
else return handler . SendChatMessage ( text ) ;
2013-07-18 09:27:19 +02:00
}
2014-06-18 13:32:17 +02:00
2014-11-11 00:55:42 +11:00
/// <summary>
2014-11-10 20:43:00 +01:00
/// Allow to respawn after death
2014-11-11 00:55:42 +11:00
/// </summary>
2014-11-10 20:43:00 +01:00
/// <returns>True if packet successfully sent</returns>
public bool SendRespawnPacket ( )
2014-11-11 00:55:42 +11:00
{
2014-11-10 20:43:00 +01:00
return handler . SendRespawnPacket ( ) ;
2014-11-11 00:55:42 +11:00
}
2014-11-11 00:32:32 +11:00
2014-06-18 13:32:17 +02:00
/// <summary>
2014-11-10 20:43:00 +01:00
/// Triggered when a new player joins the game
2014-06-18 13:32:17 +02:00
/// </summary>
2014-11-10 20:43:00 +01:00
/// <param name="uuid">UUID of the player</param>
2016-08-27 15:46:34 +02:00
/// <param name="name">Name of the player</param>
public void OnPlayerJoin ( Guid uuid , string name )
2014-11-11 00:55:42 +11:00
{
2016-08-22 23:15:16 +02:00
//Ignore placeholders eg 0000tab# from TabListPlus
2016-08-27 15:46:34 +02:00
if ( ! ChatBot . IsValidName ( name ) )
2015-04-06 11:42:43 +02:00
return ;
2015-03-19 22:08:26 +01:00
lock ( onlinePlayers )
{
2016-08-27 15:46:34 +02:00
onlinePlayers [ uuid ] = name ;
2015-03-19 22:08:26 +01:00
}
2014-11-11 00:55:42 +11:00
}
2015-05-19 15:36:20 +01:00
2014-11-10 20:43:00 +01:00
/// <summary>
/// Triggered when a player has left the game
/// </summary>
/// <param name="uuid">UUID of the player</param>
public void OnPlayerLeave ( Guid uuid )
{
2015-03-19 22:08:26 +01:00
lock ( onlinePlayers )
{
onlinePlayers . Remove ( uuid ) ;
}
2014-11-11 00:55:42 +11:00
}
2014-11-10 20:43:00 +01:00
/// <summary>
/// Get a set of online player names
/// </summary>
/// <returns>Online player names</returns>
2016-08-27 15:46:34 +02:00
public string [ ] GetOnlinePlayers ( )
2014-11-10 20:43:00 +01:00
{
2015-03-19 22:08:26 +01:00
lock ( onlinePlayers )
{
return onlinePlayers . Values . Distinct ( ) . ToArray ( ) ;
}
2014-11-10 20:43:00 +01:00
}
2016-02-07 14:24:01 -08:00
/// <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 ) ;
}
}
}
2014-11-11 00:55:42 +11:00
}
2013-07-18 09:27:19 +02:00
}