Implement global chat message cooldown (#661)

Merge 'splitmessagedelay' and 'botmessagedelay' settings into
a single 'messagecooldown' entry, implement global cooldown to
fix autorespond, scripts and commands not being delayed properly.
This commit is contained in:
ORelio 2020-10-24 17:41:35 +02:00
parent 73ba2d1555
commit 9169036893
5 changed files with 42 additions and 70 deletions

View file

@ -233,13 +233,13 @@ namespace MinecraftClient
/// Send text to the server. Can be anything such as chat messages or commands /// Send text to the server. Can be anything such as chat messages or commands
/// </summary> /// </summary>
/// <param name="text">Text to send to the server</param> /// <param name="text">Text to send to the server</param>
/// <returns>True if the text was sent with no error</returns> /// <returns>TRUE if successfully sent (Deprectated, always returns TRUE for compatibility purposes with existing scripts)</returns>
public bool SendText(object text) public bool SendText(object text)
{ {
bool result = base.SendText(text is string ? (string)text : text.ToString()); base.SendText(text is string ? (string)text : text.ToString());
tickHandler.WaitOne(); tickHandler.WaitOne();
Thread.Sleep(1000); Thread.Sleep(1000);
return result; return true;
} }
/// <summary> /// <summary>

View file

@ -16,7 +16,7 @@ namespace MinecraftClient
/// Inherit from this class while adding your bot class to the "ChatBots" folder. /// Inherit from this class while adding your bot class to the "ChatBots" folder.
/// Override the methods you want for handling events: Initialize, Update, GetText. /// Override the methods you want for handling events: Initialize, Update, GetText.
/// ///
/// For testing your bot you can add it in McTcpClient.cs (see comment at line ~119). /// For testing your bot you can add it in McClient.cs (see comment at line ~199).
/// Your bot will be loaded everytime MCC is started so that you can test/debug. /// Your bot will be loaded everytime MCC is started so that you can test/debug.
/// ///
/// Once your bot is fully written and tested, you can export it a standalone script. /// Once your bot is fully written and tested, you can export it a standalone script.
@ -40,8 +40,6 @@ namespace MinecraftClient
private McClient _handler = null; private McClient _handler = null;
private ChatBot master = null; private ChatBot master = null;
private List<string> registeredPluginChannels = new List<String>(); private List<string> registeredPluginChannels = new List<String>();
private Queue<string> chatQueue = new Queue<string>();
private DateTime lastMessageSentTime = DateTime.MinValue;
private McClient Handler private McClient Handler
{ {
get get
@ -53,30 +51,6 @@ namespace MinecraftClient
throw new InvalidOperationException(Translations.Get("exception.chatbot.init")); throw new InvalidOperationException(Translations.Get("exception.chatbot.init"));
} }
} }
private bool MessageCooldownEnded
{
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 (MessageCooldownEnded)
{
string text = chatQueue.Dequeue();
LogToConsole("Sending '" + text + "'");
lastMessageSentTime = DateTime.Now;
Handler.SendText(text);
}
}
}
/* ================================================== */ /* ================================================== */
/* Main methods to override for creating your bot */ /* Main methods to override for creating your bot */
@ -375,23 +349,12 @@ namespace MinecraftClient
/// </summary> /// </summary>
/// <param name="text">Text to send to the server</param> /// <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> /// <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> /// <returns>TRUE if successfully sent (Deprectated, always returns TRUE for compatibility purposes with existing scripts)</returns>
protected bool SendText(string text, bool sendImmediately = false) protected bool SendText(string text, bool sendImmediately = false)
{ {
if (Settings.botMessageDelay.TotalSeconds > 0 && !sendImmediately)
{
if (!MessageCooldownEnded)
{
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 + "'"); LogToConsole("Sending '" + text + "'");
lastMessageSentTime = DateTime.Now; Handler.SendText(text);
return Handler.SendText(text); return true;
} }
/// <summary> /// <summary>

View file

@ -27,6 +27,9 @@ namespace MinecraftClient
private readonly Dictionary<Guid, string> onlinePlayers = new Dictionary<Guid, string>(); private readonly Dictionary<Guid, string> onlinePlayers = new Dictionary<Guid, string>();
private static bool CommandLoaded = false; private static bool CommandLoaded = false;
private Queue<string> chatQueue = new Queue<string>();
private static DateTime nextMessageSendTime = DateTime.MinValue;
private readonly List<ChatBot> bots = new List<ChatBot>(); private readonly List<ChatBot> bots = new List<ChatBot>();
private static readonly List<ChatBot> botsOnHold = new List<ChatBot>(); private static readonly List<ChatBot> botsOnHold = new List<ChatBot>();
private static Dictionary<int, Container> inventories = new Dictionary<int, Container>(); private static Dictionary<int, Container> inventories = new Dictionary<int, Container>();
@ -530,7 +533,6 @@ namespace MinecraftClient
try try
{ {
bot.Update(); bot.Update();
bot.ProcessQueuedText();
} }
catch (Exception e) catch (Exception e)
{ {
@ -542,6 +544,16 @@ namespace MinecraftClient
} }
} }
lock (chatQueue)
{
if (chatQueue.Count > 0 && nextMessageSendTime < DateTime.Now)
{
string text = chatQueue.Dequeue();
handler.SendChatMessage(text);
nextMessageSendTime = DateTime.Now + Settings.messageCooldown;
}
}
if (terrainAndMovementsEnabled && locationReceived) if (terrainAndMovementsEnabled && locationReceived)
{ {
lock (locationLock) lock (locationLock)
@ -886,32 +898,32 @@ namespace MinecraftClient
/// Send a chat message or command to the server /// Send a chat message or command to the server
/// </summary> /// </summary>
/// <param name="text">Text to send to the server</param> /// <param name="text">Text to send to the server</param>
/// <returns>True if the text was sent with no error</returns> public void SendText(string text)
public bool SendText(string text)
{ {
int maxLength = handler.GetMaxChatMessageLength(); lock (chatQueue)
if (text.Length > maxLength) //Message is too long?
{ {
if (text[0] == '/') int maxLength = handler.GetMaxChatMessageLength();
if (text.Length > maxLength) //Message is too long?
{ {
//Send the first 100/256 chars of the command if (text[0] == '/')
text = text.Substring(0, maxLength);
return handler.SendChatMessage(text);
}
else
{
//Send the message splitted into several messages
while (text.Length > maxLength)
{ {
handler.SendChatMessage(text.Substring(0, maxLength)); //Send the first 100/256 chars of the command
text = text.Substring(maxLength, text.Length - maxLength); text = text.Substring(0, maxLength);
if (Settings.splitMessageDelay.TotalSeconds > 0) chatQueue.Enqueue(text);
Thread.Sleep(Settings.splitMessageDelay); }
else
{
//Split the message into several messages
while (text.Length > maxLength)
{
chatQueue.Enqueue(text.Substring(0, maxLength));
text = text.Substring(maxLength, text.Length - maxLength);
}
chatQueue.Enqueue(text);
} }
return handler.SendChatMessage(text);
} }
else chatQueue.Enqueue(text);
} }
else return handler.SendChatMessage(text);
} }
/// <summary> /// <summary>

View file

@ -15,9 +15,8 @@ serverip=
language=en_GB language=en_GB
consoletitle=%username%@%serverip% - Minecraft Console Client consoletitle=%username%@%serverip% - Minecraft Console Client
internalcmdchar=slash # Use 'none', 'slash' or 'backslash' internalcmdchar=slash # Use 'none', 'slash' or 'backslash'
splitmessagedelay=2 # Seconds between each part of a long message messagecooldown=1 # Minimum delay in seconds between messages to avoid being kicked for spam.
botowners=Player1,Player2,Player3 # Name list or myfile.txt, one name per line. !Server admins can impersonate owners! botowners=Player1,Player2,Player3 # Name list or myfile.txt, one name per line. !Server admins can impersonate owners!
botmessagedelay=2 # Seconds to delay between message a bot makes to avoid accidental spam
mcversion=auto # Use 'auto' or '1.X.X' values. Allows to skip server info retrieval. mcversion=auto # Use 'auto' or '1.X.X' values. Allows to skip server info retrieval.
mcforge=auto # Use 'auto', 'false' or 'true'. Force-enabling only works for MC 1.13+. mcforge=auto # Use 'auto', 'false' or 'true'. Force-enabling only works for MC 1.13+.
brandinfo=mcc # Use 'mcc', 'vanilla', or 'none'. This is how MCC identifies itself to the server. brandinfo=mcc # Use 'mcc', 'vanilla', or 'none'. This is how MCC identifies itself to the server.

View file

@ -77,9 +77,8 @@ namespace MinecraftClient
public static string TranslationsFile_FromMCDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\.minecraft\assets\objects\eb\ebf762c137bd91ab2496397f2504e250f3c5d1ba"; //MC 1.16 en_GB.lang public static string TranslationsFile_FromMCDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\.minecraft\assets\objects\eb\ebf762c137bd91ab2496397f2504e250f3c5d1ba"; //MC 1.16 en_GB.lang
public static string TranslationsFile_Website_Index = "https://launchermeta.mojang.com/v1/packages/bdb68de96a44ec1e9ed6d9cfcd2ee973be618c3a/1.16.json"; public static string TranslationsFile_Website_Index = "https://launchermeta.mojang.com/v1/packages/bdb68de96a44ec1e9ed6d9cfcd2ee973be618c3a/1.16.json";
public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net"; public static string TranslationsFile_Website_Download = "http://resources.download.minecraft.net";
public static TimeSpan splitMessageDelay = TimeSpan.FromSeconds(2); public static TimeSpan messageCooldown = TimeSpan.FromSeconds(2);
public static List<string> Bots_Owners = new List<string>(); public static List<string> Bots_Owners = new List<string>();
public static TimeSpan botMessageDelay = TimeSpan.FromSeconds(2);
public static string Language = "en_GB"; public static string Language = "en_GB";
public static bool interactiveMode = true; public static bool interactiveMode = true;
public static char internalCmdChar = '/'; public static char internalCmdChar = '/';
@ -271,7 +270,7 @@ namespace MinecraftClient
case "playerheadicon": playerHeadAsIcon = str2bool(argValue); break; case "playerheadicon": playerHeadAsIcon = str2bool(argValue); break;
case "chatbotlogfile": chatbotLogFile = argValue; break; case "chatbotlogfile": chatbotLogFile = argValue; break;
case "mcversion": ServerVersion = argValue; break; case "mcversion": ServerVersion = argValue; break;
case "splitmessagedelay": splitMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break; case "messagecooldown": messageCooldown = TimeSpan.FromSeconds(str2int(argValue)); break;
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;
@ -281,7 +280,6 @@ namespace MinecraftClient
case "enableentityhandling": EntityHandling = str2bool(argValue); break; case "enableentityhandling": EntityHandling = str2bool(argValue); break;
case "inventoryhandling": InventoryHandling = str2bool(argValue); break; case "inventoryhandling": InventoryHandling = str2bool(argValue); break;
case "privatemsgscmdname": PrivateMsgsCmdName = argValue.ToLower().Trim(); break; case "privatemsgscmdname": PrivateMsgsCmdName = argValue.ToLower().Trim(); break;
case "botmessagedelay": botMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break;
case "debugmessages": DebugMessages = str2bool(argValue); break; case "debugmessages": DebugMessages = str2bool(argValue); break;
case "autorespawn": AutoRespawn = str2bool(argValue); break; case "autorespawn": AutoRespawn = str2bool(argValue); break;