/// <summary>
        /// Sends a command string to the mud and does not do typical line processing like splitting commands, identifying
        /// if an alias was run, identifying if a hash command was run or tracking the spam guard.
        /// </summary>
        /// <param name="cmd">The raw unprocessed command to send.</param>
        /// <param name="silent">Whether the commands should be outputted to the game window.</param>
        public async Task SendRaw(string cmd, bool silent)
        {
            if (Telnet == null)
            {
                Conveyor.EchoLog("You are not connected to the game.", LogType.Error);
                return;
            }

            // Show the command in the window that was sent.
            if (!silent)
            {
                EchoText(cmd, AnsiColors.Yellow);
            }

            try
            {
                await Telnet.SendAsync(cmd);
            }
            catch (Exception ex)
            {
                App.Conveyor.EchoError(ex.Message);

                if (this.Telnet == null || !this.Telnet.IsConnected())
                {
                    App.Conveyor.SetText("Disconnected from server.");
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Connects to the mud server.  Requires that the event handlers for required events be passed in here where they will
        /// be wired up.
        /// </summary>
        public async void Connect(EventHandler <string> lineReceived, EventHandler <string> dataReceived, EventHandler connectionClosed)
        {
            try
            {
                if (Telnet != null)
                {
                    Telnet.Dispose();
                    Telnet = null;
                }

                Conveyor.EchoLog($"Connecting: {App.Settings.ProfileSettings.IpAddress}:{App.Settings.ProfileSettings.Port}", LogType.Information);

                var ctc = new CancellationTokenSource();
                Telnet = new TelnetClient(App.Settings.ProfileSettings.IpAddress, App.Settings.ProfileSettings.Port, TimeSpan.FromSeconds(0), ctc.Token);
                Telnet.ConnectionClosed += connectionClosed;
                Telnet.LineReceived     += lineReceived;
                Telnet.DataReceived     += dataReceived;
                await Telnet.Connect();
            }
            catch (Exception ex)
            {
                Telnet.Dispose();
                Conveyor.EchoLog($"Connection Failed: {ex.Message}", LogType.Error);
            }
        }
        /// <summary>
        /// Connects to the mud server.  Requires that the event handlers for required events be passed in here where they will
        /// be wired up.
        /// </summary>
        public async Task Connect(EventHandler <string> lineReceived, EventHandler <string> dataReceived, EventHandler connectionClosed)
        {
            try
            {
                if (Telnet != null)
                {
                    Telnet.Dispose();
                    Telnet = null;
                }

                // If there was a last host and it was not the current IP to connect to it likely means
                // a new profile was loaded, in that case we're going to reset the ScriptingHost to it's default
                // so things aren't hanging around.
                if (!string.IsNullOrWhiteSpace(_lastHost) && !string.Equals(_lastHost, App.Settings.ProfileSettings.IpAddress))
                {
                    this.ScriptHost?.Reset();
                    Conveyor.EchoLog("Host change detected: Scripting environment reset.", LogType.Information);

                    // Refresh the scripts so they will load when needed.
                    this.ScriptHost?.RefreshScripts();
                }

                // We can set this now, when we come back in if the IP changes they we'll reset above.
                _lastHost = App.Settings.ProfileSettings.IpAddress;

                Conveyor.EchoLog($"Connecting: {App.Settings.ProfileSettings.IpAddress}:{App.Settings.ProfileSettings.Port}", LogType.Information);

                var ctc = new CancellationTokenSource();
                this.Telnet = new TelnetClient(App.Settings.ProfileSettings.IpAddress, App.Settings.ProfileSettings.Port, TimeSpan.FromSeconds(0), ctc.Token);
                this.Telnet.ConnectionClosed += connectionClosed;
                this.Telnet.LineReceived     += lineReceived;
                this.Telnet.DataReceived     += dataReceived;
                await this.Telnet.ConnectAsync();
            }
            catch (Exception ex)
            {
                Telnet?.Dispose();
                Conveyor.EchoLog($"Connection Failed: {ex.Message}", LogType.Error);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Sends a command string to the mud.
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="silent">Whether the commands should be outputted to the game window.</param>
        public async Task Send(string cmd, bool silent, bool addToInputHistory)
        {
            // Add the whole thing to the command history
            if (addToInputHistory)
            {
                AddInputHistory(cmd);
            }

            _aliasRecursionDepth = 0;
            var cmdList = ParseCommand(cmd);

            foreach (string item in cmdList)
            {
                if (Telnet == null)
                {
                    Conveyor.EchoLog("You are not connected to the game.", LogType.Error);
                    return;
                }

                try
                {
                    if (item.SafeLeft(1) != "#")
                    {
                        // Show the command in the window that was sent.
                        if (!silent)
                        {
                            EchoText(item, AnsiColors.Yellow);
                        }

                        // Spam guard
                        if (App.Settings.ProfileSettings.SpamGuard)
                        {
                            // TODO, make this check carriage return and > 1 instead of > 2
                            if (_spamGuardLastCommand == item && item.Length > 2)
                            {
                                // Increment, we don't need to change the last command, it was the same.
                                _spamGuardCounter++;
                            }
                            else
                            {
                                _spamGuardLastCommand = item;
                                _spamGuardCounter     = 0;
                            }

                            if (_spamGuardCounter == 15)
                            {
                                EchoText("where", AnsiColors.Yellow);
                                await Telnet.Send("where");

                                _spamGuardCounter = 0;
                            }
                        }


                        await Telnet.Send(item);
                    }
                    else
                    {
                        var hashCmd = HashCommands.FirstOrDefault(x => x.Name == item.FirstWord());

                        if (hashCmd == null)
                        {
                            Conveyor.EchoLog($"Hash command '{item.FirstWord()}' was not found.\r\n", LogType.Error);
                        }
                        else
                        {
                            string parameters = item.RemoveWord(1);
                            hashCmd.Parameters = parameters;

                            if (hashCmd.IsAsync)
                            {
                                await hashCmd.ExecuteAsync();
                            }
                            else
                            {
                                hashCmd.Execute();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    EchoText(ex.Message, AnsiColors.Red);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Parses a command, also adds it to the history list.
        /// </summary>
        /// <param name="cmd"></param>
        public List <string> ParseCommand(string cmd)
        {
            _aliasRecursionDepth += 1;

            if (_aliasRecursionDepth >= 10)
            {
                Conveyor.EchoLog($"Alias error: Reached max recursion depth of {_aliasRecursionDepth}.\r\n", LogType.Error);
                return(new List <string>());
            }

            // Swap known variables out, both system and user defined. Strings are immutable but if no variable
            // is found it won't create a new string, it will return the reference to the current one.
            cmd = this.Conveyor.ReplaceVariablesWithValue(cmd);

            // Get the current character
            string characterName = Conveyor.GetVariable("Character");

            // Split the list
            var initialList = cmd.Split(';').ToList();
            var list        = new List <string>();

            foreach (var item in initialList)
            {
                var first = item.FirstArgument();
                var alias = App.Settings.ProfileSettings.AliasList.FirstOrDefault(x => x.AliasExpression == first.Item1 && x.Enabled == true && (string.IsNullOrEmpty(x.Character) || x.Character == characterName));

                if (alias == null)
                {
                    // No alias was found, just add the item.
                    list.Add(item);
                }
                else
                {
                    // See if the aliases are globally disabled.  We're putting this here so we can let the user know they
                    // tried to use an alias but they're disabled.
                    if (!App.Settings.ProfileSettings.AliasesEnabled)
                    {
                        EchoText($"--> Alias found but aliases are globally disabled.", AnsiColors.Red);
                        list.Add(item);
                        continue;
                    }

                    // Increment that we used it
                    alias.Count++;

                    // If the alias is defined as Lua it will be processed verbatim.  A lua alias anywhere in the command short circuits
                    // any other input and only that Lua command gets run (and only the first one).
                    // TODO: Make this work better.  Is an alias the right way to do this?
                    if (alias.IsLua)
                    {
                        list.Clear();

                        // TODO: Put this into it's own function.
                        // Alias where the arguments are specified, we will support up to 9 arguments at this time.
                        string lua = alias.Command;

                        // %0 will represent the entire matched string.
                        lua = lua.Replace("%0", first.Item2.Replace("\"", "\\\""));

                        // %1-%9
                        for (int i = 1; i <= 9; i++)
                        {
                            lua = lua.Replace($"%{i}", first.Item2.ParseWord(i, " ").Replace("\"", "\\\""));
                        }

                        // This is all that's going to execute as it clears the list.. we can "fire and forget".
                        this.LuaCaller.ExecuteAsync(lua);

                        return(list);
                    }

                    // We have an alias, see if it's a simple alias where we put all of the text at the end or if
                    // it's one where we'll let the user place the words where they need to be.
                    if (!alias.Command.Contains("%"))
                    {
                        list.AddRange(ParseCommand($"{alias.Command} {first.Item2}".Trim()));
                        _aliasRecursionDepth--;
                    }
                    else
                    {
                        // Alias where the arguments are specified, we will support up to 9 arguments at this time.
                        string aliasStr = alias.Command;

                        // %0 will represent the entire matched string.
                        aliasStr = aliasStr.Replace("%0", first.Item2);

                        // %1-%9
                        for (int i = 1; i <= 9; i++)
                        {
                            aliasStr = aliasStr.Replace($"%{i}", first.Item2.ParseWord(i, " "));
                        }

                        list.AddRange(ParseCommand(aliasStr));
                        _aliasRecursionDepth--;
                    }
                }
            }

            // Return the final list.
            return(list);
        }
        /// <summary>
        /// Parses a command, also adds it to the history list.
        /// </summary>
        /// <param name="cmd"></param>
        public List <string> ParseCommand(string cmd)
        {
            _aliasRecursionDepth += 1;

            if (_aliasRecursionDepth >= 10)
            {
                Conveyor.EchoLog($"Alias error: Reached max recursion depth of {_aliasRecursionDepth.ToString()}.\r\n", LogType.Error);
                return(new List <string>());
            }

            // Swap known variables out, both system and user defined. Strings are immutable but if no variable
            // is found it won't create a new string, it will return the reference to the current one.
            cmd = this.Conveyor.ReplaceVariablesWithValue(cmd);

            // Get the current character
            string characterName = Conveyor.GetVariable("Character");

            // Split the list
            var list = new List <string>();

            foreach (var item in cmd.Split(App.Settings.AvalonSettings.CommandSplitCharacter))
            {
                var first = item.FirstArgument();

                Alias?alias = null;

                for (int index = 0; index < App.Settings.ProfileSettings.AliasList.Count; index++)
                {
                    var x = App.Settings.ProfileSettings.AliasList[index];

                    if (x.AliasExpression == first.Item1 && x.Enabled && (string.IsNullOrEmpty(x.Character) || x.Character == characterName))
                    {
                        alias = x;
                        break;
                    }
                }

                if (alias == null)
                {
                    // No alias was found, just add the item.
                    list.Add(item);
                }
                else
                {
                    // See if the aliases are globally disabled.  We're putting this here so we can let the user know they
                    // tried to use an alias but they're disabled.
                    if (!App.Settings.ProfileSettings.AliasesEnabled)
                    {
                        this.Conveyor.EchoError($"Alias found for '{alias.AliasExpression ?? "null"}' but aliases are globally disabled.");
                        list.Add(item);
                        continue;
                    }

                    // Increment that we used it
                    alias.Count++;

                    // If the alias is Lua then variables will be swapped in if necessary and then executed.
                    if (alias.IsLua || alias.ExecuteAs == ExecuteType.LuaMoonsharp)
                    {
                        list.Clear();
                        _ = this.ScriptHost.MoonSharp.ExecuteFunctionAsync <object>(alias.FunctionName, item.Split(' ', StringSplitOptions.RemoveEmptyEntries));

                        return(list);
                    }

                    // We have an alias, see if it's a simple alias where we put all of the text at the end or if
                    // it's one where we'll let the user place the words where they need to be.
                    if (!alias.Command.Contains('%'))
                    {
                        list.AddRange(ParseCommand($"{alias.Command} {first.Item2}".Trim()));
                        _aliasRecursionDepth--;
                    }
                    else
                    {
                        // Alias where the arguments are specified, we will support up to 9 arguments at this time.
                        if (alias.Command.Contains("%", StringComparison.Ordinal))
                        {
                            var sb = ZString.CreateStringBuilder();
                            sb.Append(alias.Command);

                            // %0 will represent the entire matched string.
                            sb.Replace("%0", first.Item2);

                            // %1-%9
                            for (int i = 1; i <= 9; i++)
                            {
                                sb.Replace($"%{i.ToString()}", first.Item2.ParseWord(i, " "));
                            }

                            list.AddRange(ParseCommand(sb.ToString()));
                            sb.Dispose();

                            _aliasRecursionDepth--;
                        }
                        else
                        {
                            list.AddRange(ParseCommand(alias.Command));
                            _aliasRecursionDepth--;
                        }
                    }
                }
            }

            // Return the final list.
            return(list);
        }
        /// <summary>
        /// Sends a command string to the mud.
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="silent">Whether the commands should be outputted to the game window.</param>
        /// <param name="addToInputHistory">Whether the command should be added to the input history the user can scroll back through.</param>
        public async Task Send(string cmd, bool silent, bool addToInputHistory)
        {
            // Add the whole thing to the command history
            if (addToInputHistory)
            {
                AddInputHistory(cmd);
            }

            _aliasRecursionDepth = 0;

            foreach (string item in ParseCommand(cmd))
            {
                if (Telnet == null)
                {
                    Conveyor.EchoLog("You are not connected to the game.", LogType.Error);
                    return;
                }

                try
                {
                    if (!item.StartsWith('#'))
                    {
                        // Show the command in the window that was sent.
                        if (!silent)
                        {
                            EchoText(item, AnsiColors.Yellow);
                        }

                        // Spam guard
                        if (App.Settings.ProfileSettings.SpamGuard)
                        {
                            if (_spamGuardLastCommand == item && item.Length > 2)
                            {
                                // Increment, we don't need to change the last command, it was the same.
                                _spamGuardCounter++;
                            }
                            else
                            {
                                _spamGuardLastCommand = item;
                                _spamGuardCounter     = 0;
                            }

                            if (_spamGuardCounter == 15)
                            {
                                EchoText(App.Settings.ProfileSettings.SpamGuardCommand, AnsiColors.Yellow);
                                await Telnet.SendAsync(App.Settings.ProfileSettings.SpamGuardCommand);

                                _spamGuardCounter = 0;
                            }
                        }

                        await Telnet.SendAsync(item);
                    }
                    else
                    {
                        string firstWord = item.FirstWord();

                        // Avoided a closure allocation by loop instead of using Linq.
                        IHashCommand?hashCmd = null;

                        for (int index = 0; index < this.HashCommands.Count; index++)
                        {
                            var x = this.HashCommands[index];

                            if (x.Name.Equals(firstWord, StringComparison.Ordinal))
                            {
                                hashCmd = x;
                                break;
                            }
                        }

                        if (hashCmd == null)
                        {
                            Conveyor.EchoLog($"Hash command '{firstWord}' was not found.\r\n", LogType.Error);
                        }
                        else
                        {
                            hashCmd.Parameters = item.RemoveWord(1);

                            if (hashCmd.IsAsync)
                            {
                                await hashCmd.ExecuteAsync();
                            }
                            else
                            {
                                // ReSharper disable once MethodHasAsyncOverload
                                hashCmd.Execute();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    App.Conveyor.EchoError(ex.Message);

                    if (this.Telnet == null || !this.Telnet.IsConnected())
                    {
                        App.Conveyor.SetText("Disconnected from server.", TextTarget.StatusBarText);
                    }
                }
            }

            // Add words to our unique HashSet if the settings allow for it (and after the
            // commands for the game have been sent.
            if (App.Settings.AvalonSettings.AutoCompleteWord && addToInputHistory)
            {
                this.InputAutoCompleteKeywords.AddWords(cmd);
            }
        }
Beispiel #8
0
        /// <summary>
        /// The Loaded event for the Window where we will execute code that should run when the Window
        /// is first put into place.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // The Conveyor will be passed around to other objects so that they can interact with the UI.  This Conveyor may have
            // state so it's important to re-use this object unless sandboxing is needed.
            Conveyor = new Conveyor();

            // The settings for the app load in the app startup, they will then try to load the last profile
            // that was used.
            Conveyor.EchoLog($"Settings Folder: {App.Settings.AppDataDirectory}", LogType.Information);
            Conveyor.EchoLog($"Settings File:   {App.Settings.AvalonSettingsFile}", LogType.Information);
            Conveyor.EchoLog($"Profiles Folder: {App.Settings.AvalonSettings.SaveDirectory}", LogType.Information);

            // Parse the command line arguments to see if a profile was specified.
            var args = Environment.GetCommandLineArgs();


            // Try to load the last profile loaded, if not found create a new profile.
            if (File.Exists(App.Settings.AvalonSettings.LastLoadedProfilePath))
            {
                App.Settings.LoadSettings(App.Settings.AvalonSettings.LastLoadedProfilePath);
                Conveyor.EchoLog($"Settings Loaded: {App.Settings.AvalonSettings.LastLoadedProfilePath}", LogType.Information);
            }
            else
            {
                if (string.IsNullOrWhiteSpace(App.Settings.AvalonSettings.LastLoadedProfilePath))
                {
                    Conveyor.EchoLog($"New Profile being created.", LogType.Information);
                }
                else
                {
                    Conveyor.EchoLog($"Last Profile Loaded Not Found: {App.Settings.AvalonSettings.LastLoadedProfilePath}", LogType.Warning);
                }
            }


            // TODO - Figure out a better way to inject a single Conveyor, maybe static in App?
            // Inject the Conveyor into the Triggers.
            foreach (var trigger in App.Settings.ProfileSettings.TriggerList)
            {
                trigger.Conveyor = Conveyor;
            }

            // Wire up any events that have to be wired up through code.
            TextInput.Editor.PreviewKeyDown += this.Editor_PreviewKeyDown;
            AddHandler(TabControlEx.NetworkButtonClickEvent, new RoutedEventHandler(NetworkButton_Click));
            AddHandler(TabControlEx.SettingsButtonClickEvent, new RoutedEventHandler(SettingsButton_Click));

            // Pass the necessary reference from this page to the Interpreter.
            Interp = new Interpreter(this.Conveyor);

            // Setup the handler so when it wants to write to the main window it can by raising the echo event.
            Interp.Echo += this.InterpreterEcho;

            // Setup Lua
            Lua = new Script();
            Lua.Options.CheckThreadAccess = false;
            UserData.RegisterType <LuaCommands>();

            // create a userdata, again, explicitly.
            var luaCmd = UserData.Create(new LuaCommands(Interp));

            Lua.Globals.Set("Cmd", luaCmd);
            LuaControl = new ExecutionControlToken();

            // Setup the tick timer.
            TickTimer = new TickTimer(Conveyor);

            // Setup the auto complete commands.  If they're found refresh them, if they're not
            // report it to the terminal window.  It should -always be found-.
            RefreshAutoCompleteEntries();

            // Auto connect to the game if the setting is set.
            if (App.Settings.ProfileSettings.AutoConnect)
            {
                NetworkButton_Click(null, null);
            }

            // Is there an auto execute command or set of commands to run?
            if (!string.IsNullOrWhiteSpace(App.Settings.ProfileSettings.AutoExecuteCommand))
            {
                Interp.Send(App.Settings.ProfileSettings.AutoExecuteCommand, true, false);
            }

            // Finally, all is done, set the focus to the command box.
            TextInput.Focus();
        }
Beispiel #9
0
 /// <summary>
 /// Handles when a connection is closed.
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 public void HandleConnectionClosed(object sender, EventArgs e)
 {
     TabMain.IsConnected = false;
     Conveyor.EchoLog($"Disconnected: {DateTime.Now}", LogType.Warning);
     Interp.Telnet = null;
 }