Esempio n. 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SynServerTool" /> main class.
        /// </summary>
        /// <param name="isRestart">if set to <c>true</c> then the application was
        /// launched for the process of restarting previous monitoring.</param>
        /// <remarks>
        /// Some notes/thoughts: All of the core functions of the bot including console text
        /// processing, player/server event processing, modules, command processing, vote
        /// management, parsing, etc. are initialized in this constructor and set as properties in
        /// this main class. The bot only allows one instance of itself for the explicit reason that
        /// Quake Live can only have one running copy open at a time. For this reason, this
        /// initilizated <see cref="SynServerTool" /> object is frequently passed around the rest of
        /// the code almost entirely through constructor injection and the properties are directly
        /// accessed rather than constantly instantiating new classes. In this application, access
        /// to state among most parts is crucial, and unfortunately that leads to some unavoidable
        /// tight coupling. Once intilizated, the bot will then call the
        /// <see cref="CheckForAutoMonitoring" /> method which reads the configuration to see if the
        /// user has specified whether server monitoring should begin on application start. If Quake
        /// Live is running, we will check to see if the client is connected to a server. If
        /// connected, we will retrieve the server information and players using built in QL
        /// commands. After that, we will start a timer that waits for ~6.5s to perform any final
        /// initlization tasks to make sure all necessary information is present. This project
        /// initially started as a VERY simple proof of concept and expanded dramatically from
        /// there, so refactoring in various places is almost certainly in order. For example, a
        /// user interface was not initially planned (the tool was going to only be command-driven
        /// in-game), but was later added during development for ease of use.
        /// </remarks>
        public SynServerTool(bool isRestart)
        {
            // Core
            ServerInfo           = new ServerInfo();
            QlCommands           = new QlCommands(this);
            Parser               = new Parser();
            QlWindowUtils        = new QlWindowUtils();
            ConsoleTextProcessor = new ConsoleTextProcessor(this);
            ServerEventProcessor = new ServerEventProcessor(this);
            VoteManager          = new VoteManager();

            //Set the name of the bot
            AccountName = GetAccountNameFromConfig();
            // Hook up modules
            Mod = new ModuleManager(this);
            // Hook up command listener
            CommandProcessor = new CommandProcessor(this);

            // If being launched as restart then automatically try to start monitoring and skip the check.
            if (isRestart)
            {
                // ReSharper disable once UnusedVariable (synchronous)
                var a = AttemptAutoMonitorStart();
            }
            else
            {
                // Otherwise, check if we should begin monitoring a server immediately per user's settings.
                CheckForAutoMonitoring();
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Handles the player chat message.
        /// </summary>
        /// <param name="text">The text.</param>
        public void HandlePlayerChatMessage(string text)
        {
            // Typically we'd normalize using ToUpperInvariant() but QL doesn't allow accented
            // characters, so it doesn't matter
            var msgContent =
                ConsoleTextProcessor.Strip(text.Substring(text.IndexOf(": ", StringComparison.Ordinal) + 1))
                .ToLowerInvariant();

            var name = text.Substring(0, text.LastIndexOf('\u0019'));

            // teamchat is already ignored, so also ignore 'tell' messages which would crash bot
            if (name.StartsWith("\u0019["))
            {
                return;
            }

            var msgFrom = Helpers.GetStrippedName(name);

            if (!_sst.AccountName.Equals(msgFrom, StringComparison.InvariantCultureIgnoreCase))
            {
                Log.Write(string.Format("Detected chat message {0} from {1}",
                                        msgContent, msgFrom), _logClassType, _logPrefix);
            }

            // If IRC module is active, send the message to the IRC channel
            if (_sst.Mod.Irc.Active && _sst.Mod.Irc.IsConnectedToIrc)
            {
                // Don't show
                if (msgContent.StartsWith("[irc]", StringComparison.InvariantCultureIgnoreCase))
                {
                    return;
                }

                _sst.Mod.Irc.IrcManager.SendIrcMessage(_sst.Mod.Irc.IrcManager.IrcSettings.ircChannel,
                                                       string.Format("[QL] {0}: {1}", msgFrom, msgContent));
            }

            // Check to see if chat message is a valid command
            if (msgContent.StartsWith(CommandList.GameCommandPrefix))
            {
                // ReSharper disable once UnusedVariable (synchronous)
                var s = _sst.CommandProcessor.HandleBotCommand(msgFrom, msgContent);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Reads the QL console window.
        /// </summary>
        private void ReadQlConsole()
        {
            var consoleWindow = QlWindowUtils.GetQuakeLiveConsoleWindow();
            var cText         = QlWindowUtils.GetQuakeLiveConsoleTextArea(consoleWindow,
                                                                          QlWindowUtils.GetQuakeLiveConsoleInputArea(consoleWindow));

            if (cText != IntPtr.Zero)
            {
                while (IsReadingConsole)
                {
                    var textLength = Win32Api.SendMessage(cText, Win32Api.WM_GETTEXTLENGTH, IntPtr.Zero,
                                                          IntPtr.Zero);
                    if ((textLength == 0) || (ConsoleTextProcessor.OldWholeConsoleLineLength == textLength))
                    {
                        continue;
                    }

                    // Entire console window text
                    var entireBuffer = new StringBuilder(textLength + 1);
                    Win32Api.SendMessage(cText, Win32Api.WM_GETTEXT, new IntPtr(textLength + 1), entireBuffer);
                    var received = entireBuffer.ToString();
                    ConsoleTextProcessor.ProcessEntireConsoleText(received, textLength);

                    var lengthDifference = Math.Abs(textLength - _oldLength);

                    if (received.Length > lengthDifference)
                    {
                        // Bounds checking
                        int start;
                        int length;

                        if (_oldLength > received.Length)
                        {
                            start  = 0;
                            length = received.Length;
                        }
                        else
                        {
                            start  = _oldLength;
                            length = lengthDifference;
                        }

                        // Standardize QL's annoying string formatting
                        var diffBuilder = new StringBuilder(received.Substring(start, length));
                        diffBuilder.Replace("\"\r\n\r\n", "\"\r\n");
                        diffBuilder.Replace("\r\n\"\r\n", "\r\n");
                        diffBuilder.Replace("\r\n\r\n", "\r\n");
                        ConsoleTextProcessor.ProcessShortConsoleLines(diffBuilder.ToString());
                    }

                    // Detect when buffer is about to be full, in order to auto-clear. Win Edit
                    // controls can have a max of 30,000 characters, see: "Limits of Edit Controls"
                    // - http://msdn.microsoft.com/en-us/library/ms997530.aspx More info: Q3 source
                    // (win_syscon.c), Conbuf_AppendText
                    int begin, end;
                    Win32Api.SendMessage(cText, Win32Api.EM_GETSEL, out begin, out end);
                    if ((begin >= 29300) && (end >= 29300))
                    {
                        Log.Write("Clearing nearly full conbuf.",
                                  _logClassType, _logPrefix);

                        // Auto-clear
                        QlCommands.ClearQlWinConsole();
                    }
                    _oldLength = textLength;
                }
            }
            else
            {
                Log.WriteCritical("Unable to get necessary console handle.", _logClassType, _logPrefix);
            }
        }