diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs
index 31c6a451..8e613e86 100644
--- a/MinecraftClient/McTcpClient.cs
+++ b/MinecraftClient/McTcpClient.cs
@@ -52,6 +52,8 @@ namespace MinecraftClient
private string uuid;
private string sessionid;
private Inventory playerInventory;
+ private DateTime lastKeepAlive;
+ private object lastKeepAliveLock = new object();
public int GetServerPort() { return port; }
public string GetServerHost() { return host; }
@@ -64,6 +66,7 @@ namespace MinecraftClient
TcpClient client;
IMinecraftCom handler;
Thread cmdprompt;
+ Thread timeoutdetector;
///
/// Starts the main chat client
@@ -167,6 +170,10 @@ namespace MinecraftClient
cmdprompt = new Thread(new ThreadStart(CommandPrompt));
cmdprompt.Name = "MCC Command prompt";
cmdprompt.Start();
+
+ timeoutdetector = new Thread(new ThreadStart(TimeoutDetector));
+ timeoutdetector.Name = "MCC Connection timeout detector";
+ timeoutdetector.Start();
}
}
}
@@ -253,6 +260,29 @@ namespace MinecraftClient
catch (NullReferenceException) { }
}
+ ///
+ /// Periodically checks for server keepalives and consider that connection has been lost if the last received keepalive is too old.
+ ///
+ private void TimeoutDetector()
+ {
+ lock (lastKeepAliveLock)
+ {
+ lastKeepAlive = DateTime.Now;
+ }
+ do
+ {
+ Thread.Sleep(TimeSpan.FromSeconds(15));
+ lock (lastKeepAliveLock)
+ {
+ if (lastKeepAlive.AddSeconds(30) < DateTime.Now)
+ {
+ OnConnectionLost(ChatBot.DisconnectReason.ConnectionLost, "Connection Timeout");
+ }
+ }
+ }
+ while (true);
+ }
+
///
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
///
@@ -336,6 +366,9 @@ namespace MinecraftClient
if (cmdprompt != null)
cmdprompt.Abort();
+ if (timeoutdetector != null)
+ timeoutdetector.Abort();
+
Thread.Sleep(1000);
if (client != null)
@@ -607,6 +640,10 @@ namespace MinecraftClient
/// Links embedded in text
public void OnTextReceived(string text, bool isJson)
{
+ lock (lastKeepAliveLock)
+ {
+ lastKeepAlive = DateTime.Now;
+ }
List links = new List();
string json = null;
if (isJson)
@@ -637,11 +674,22 @@ namespace MinecraftClient
}
}
+ ///
+ /// Received a connection keep-alive from the server
+ ///
+ public void OnServerKeepAlive()
+ {
+ lock (lastKeepAliveLock)
+ {
+ lastKeepAlive = DateTime.Now;
+ }
+ }
+
///
/// When an inventory is opened
///
/// Location to reach
- public void onInventoryOpen(Inventory inventory)
+ public void OnInventoryOpen(Inventory inventory)
{
//TODO: Handle Inventory
if (!inventories.Contains(inventory))
@@ -654,7 +702,7 @@ namespace MinecraftClient
/// When an inventory is close
///
/// Location to reach
- public void onInventoryClose(byte inventoryID)
+ public void OnInventoryClose(byte inventoryID)
{
for (int i = 0; i < inventories.Count; i++)
{
diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs
index 65a7cf49..c8516766 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol16.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs
@@ -83,6 +83,7 @@ namespace MinecraftClient.Protocol.Handlers
{
case 0x00: byte[] keepalive = new byte[5] { 0, 0, 0, 0, 0 };
Receive(keepalive, 1, 4, SocketFlags.None);
+ handler.OnServerKeepAlive();
Send(keepalive); break;
case 0x01: readData(4); readNextString(); readData(5); break;
case 0x02: readData(1); readNextString(); readNextString(); readData(4); break;
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index 403267bb..119c1bb5 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -185,6 +185,7 @@ namespace MinecraftClient.Protocol.Handlers
{
case PacketIncomingType.KeepAlive:
SendPacket(PacketOutgoingType.KeepAlive, packetData);
+ handler.OnServerKeepAlive();
break;
case PacketIncomingType.JoinGame:
handler.OnGameJoined();
@@ -481,7 +482,7 @@ namespace MinecraftClient.Protocol.Handlers
byte slots = dataTypes.ReadNextByte(packetData);
Inventory inventory = new Inventory(windowID, inventoryType, title, slots);
- handler.onInventoryOpen(inventory);
+ handler.OnInventoryOpen(inventory);
}
break;
case PacketIncomingType.CloseWindow:
@@ -489,7 +490,7 @@ namespace MinecraftClient.Protocol.Handlers
{
byte windowID = dataTypes.ReadNextByte(packetData);
- handler.onInventoryClose(windowID);
+ handler.OnInventoryClose(windowID);
}
break;
case PacketIncomingType.WindowItems:
diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs
index 3e2879de..0bcc0bcf 100644
--- a/MinecraftClient/Protocol/IMinecraftComHandler.cs
+++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs
@@ -43,15 +43,20 @@ namespace MinecraftClient.Protocol
/// TRUE if the text is JSON-Encoded
void OnTextReceived(string text, bool isJson);
+ ///
+ /// Called when receiving a connection keep-alive from the server
+ ///
+ void OnServerKeepAlive();
+
///
/// Called when an inventory is opened
///
- void onInventoryOpen(Inventory inventory);
+ void OnInventoryOpen(Inventory inventory);
///
/// Called when an inventory is closed
///
- void onInventoryClose(byte inventoryID);
+ void OnInventoryClose(byte inventoryID);
///
/// Called when the player respawns, which happens on login, respawn and world change.