#if WINDOWS #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using System.Text; using System.Threading; using System.IO; using System.ComponentModel; using System.Reflection; using XNACC.BaseTypes; using System.Diagnostics; #endregion /* * CommandConsoleBase.cs * (C) 2009-2011, James R. Twine of JRTwine Software, LLC * * ************************************************************************* * * THIS CODE IS PROVIDED WITH NO WARRANTY - YOU USE IT AT YOUR OWN RISK! * * ************************************************************************* * * This Code Originated With Code From the "XNA Console Component Sample" by Kevin Jurkowski. * At The Time Of This Writing, The Original Code Was Available * From: http://www.ziggyware.com/readarticle.php?article_id=163 * (But, Ziggyware Is No Longer There! :<) * * Changes, (C) 2009, James R. Twine of JRTwine Software, LLC * Changes: * Converted to XNA 3.0 * More input keys supported (Symbols, digits, etc.) * Better(?) Way of extending supported commands and their handlers, parameters, help, etc. * Slight Q&D colorizing of output to differenate commands and output * Extracted keyboard-specific functionality into an iterface * Command history * Command completion * Scrollable log/output * Base class supports "standard" commands * Support for "hidden" commands * Customizable scalable drawing * Custom functions (basically macros that support parameters) * Binding commands/functions to -Key combinations (when the console is closed) * * And possibly some other fun stuff...! * * More Changes, (C) 2010, James R. Twine of JRTwine Software, LLC * Changes: * Expanded key bindings to support modifiers * Converted to XNA 4.0 * Graphics optimizations * * Still MORE Changes, (C) 2011, James R. Twine of JRTwine Software, LLC * Changes: * Additional ways to hook into command execution/handling * Addition of dynamic Console Variables (cvars) * Support for external managed functions that can manipulate cvars and do... just about anything * Ability to lock-down functions, bindings and external functions (to prevent tampering with a released game) * TODO: Wanna wrap command handlers into an "ICCCommand" interface... * * * * * */ //NA: XNACC currently is only supported on Windows due to the fact that SortedDictionary is not avaiable on WP7/Xbox 360 //NA: Seriously Microsoft? /// Namespace that contains code related to the XNACC (CommandConsole) component namespace XNACC.Console { /// Base functionality of the XNACC (CommandConsole) component public class CommandConsoleBase : DrawableGameComponent { #region EventArgs Object /// EA object for the CVarModified event public class CVarModifiedEventArgs : EventArgs { /// The actual CVar for this event public CVar Value { get; protected set; } /// Constructor for this object /// The cvar that has been modified public CVarModifiedEventArgs(CVar value) { Value = value; return; } } /// EA object for handling console command execution public class CommandConsoleEventArgs : EventArgs { /// The actual command line being executed public string CmdLine { get; set; } /// Flag indicating if the command has been handled by an event handler; if true, no further processing is done on the command public bool Handled { get; set; } /// Constructor for this object /// The command line being processed public CommandConsoleEventArgs( string cmdLine ) { CmdLine = cmdLine; Handled = false; return; } } #endregion #region Command Object /// This object contains information on a single Command that the console understands. protected class CmdObject : IComparable< CmdObject > { /// Default constructor for the Command Object public CmdObject() : this( String.Empty, String.Empty, null, false, String.Empty, 0, 0 ) { return; } /// Constuct an instance of the Command Object /// The command identifier (no whitespace, please) /// The (brief) help for this command /// The delegate method that fires when the command is encountered public CmdObject( string command, string help, Action cmdAction ) : this( command, help, cmdAction, false, String.Empty, 0, 0 ) { return; } /// Constuct an instance of the Command Object /// The command identifier (no whitespace, please) /// The (brief) help for this command /// The delegate method that fires when the command is encountered /// Is this a secret command (not shown in the help information) public CmdObject( string command, string help, Action cmdAction, bool isSecret ) : this( command, help, cmdAction, isSecret, String.Empty, 0, 0 ) { return; } /// Constuct an instance of the Command Object /// The command identifier (no whitespace, please) /// The (brief) help for this command /// The delegate method that fires when the command is encountered /// Is this a secret command (not shown in the help information) /// The detailed help for this command public CmdObject( string command, string help, Action cmdAction, bool isSecret, string detailedHelp ) : this( command, help, cmdAction, isSecret, detailedHelp, 0, 0 ) { return; } /// Constuct an instance of the Command Object /// The command identifier (no whitespace, please) /// The (brief) help for this command /// The delegate method that fires when the command is encountered /// Is this a secret command (not shown in the help information) /// The detailed help for this command /// The minimum number of parameters for this command (0 == no minimum) /// The maximum number of parameters for this command (0 == no maximum) public CmdObject( string command, string help, Action cmdAction, bool isSecret, string detailedHelp, int minParams, int maxParams ) { Command = command; CommandHelp = help; if( cmdAction != null ) { CmdEvent += cmdAction; } IsSecret = isSecret; CommandHelpDetailed = detailedHelp; MinParameters = minParams; MaxParameters = maxParams; return; } /// Required - The command (single word, no spaces, and all commands will be converted to lowercase) public String Command { get; set; } /// Optional - Summary help information that briefly describes the command public String CommandHelp { get; set; } /// Optional - Detailed help information that explains the command and its usage public String CommandHelpDetailed { get; set; } /// The minimum number of parameters the command expects (set to zero for no minimum) public int MinParameters { get; set; } /// The maximum number of parameters the command expects (set to zero for no maximum) public int MaxParameters { get; set; } /// Indicates if this command is secret -- if so, it will not appear when the user types /// "help" and will not participate in command completion public bool IsSecret { get; set; } /// The event fired when the associated command is received public event Action CmdEvent; /// This method fires the associated Delegate /// The complete command line. Not just the command's parameters! /// This method is public to allow for artificial triggering of commands. public void TriggerEvent( String[] cmdLine ) { if( CmdEvent != null ) { CmdEvent.Invoke( cmdLine ); } } #region IComparable Members /// Compare this object with another object instance /// The object to compare to /// The result of comparing the object's Command strings public int CompareTo( CmdObject other ) { return( Command.CompareTo( other.Command ) ); } #endregion } #endregion #region Function Object /// This object contains information on a custom function that the console understands. protected class FuncObject : IComparable< FuncObject > { /// The function name public String Function { get; protected set; } /// The code to execute when the function executes public IList FunctionImpl { get; protected set; } /// Constructor for the Function Object /// The name of the Function /// The implementation of the Function public FuncObject( string funcName, string[] funcImpl ) { if( String.IsNullOrWhiteSpace( funcName ) ) { throw new ArgumentException( "The function name must not be whitespace.", "funcName" ); } Function = funcName.Trim().ToLowerInvariant(); FunctionImpl = new List( funcImpl ); return; } /// Indicates if this function is secret -- it will not appear when the user types "help" public bool IsSecret { get; set; } /// Provide a string representation of this object /// A string containing a representation of the object public override string ToString() { StringBuilder sbCmdLine = new StringBuilder( FunctionImpl.Count * 32 ); foreach( string line in FunctionImpl ) { if( sbCmdLine.Length > 0 ) { sbCmdLine.Append( "; " ); } sbCmdLine.Append( line ); } return ( String.Format( "{0} -> {1}", Function, sbCmdLine.ToString() ) ); } #region IComparable Members /// Compare this object with another object instance /// The object to compare to /// The result of comparing the object's Function strings public int CompareTo( FuncObject other ) { return( Function.CompareTo( other.Function ) ); } #endregion } #endregion #region External Function Object /// This object contains information on an external function (implemented in a .NET assembly) that the console understands. protected class ExternalFuncObject : IComparable { /// Collection Of Known Class Instances static protected Dictionary< string, object > ms_classInstances = new Dictionary( 8 ); /// The method name public String MethodName { get; set; } /// Is this external function secret? public bool IsSecret { get; protected set; } /// Command line used to create the function public String CommandLine { get; set; } /// The actual method metadata protected MethodInfo m_method; /// The actual class instance protected object m_classInstance; /// Construct an instance of this object, storing information on the specified method /// The name/path of the assembly/DLL to load from /// The fully qualified, case-sensitive class name /// The case-sensitive name of the method to get public ExternalFuncObject( string assembly, string className, string methodName ) : this( assembly, className, methodName, false ) { return; } /// Construct an instance of this object, storing information on the specified method /// The name/path of the assembly/DLL to load from /// The fully qualified, case-sensitive class name /// The case-sensitive name of the method to get /// Is this a secret/hidden ExFunc? public ExternalFuncObject( string assembly, string className, string methodName, bool isSecret ) { Type classType = null; // See If We Already Have An Instance Of This Class if( ms_classInstances.TryGetValue( className, out m_classInstance ) == false ) { // We Do Not, So Create And Store One Assembly asm = null; try { asm = Assembly.LoadFrom( assembly ); } catch( FileNotFoundException ) { asm = Assembly.LoadFrom( assembly + ".dll" ); } classType = asm.GetType( className, false ); if( classType == null ) { classType = asm.GetType( assembly + "." + className, true ); } m_classInstance = Activator.CreateInstance( classType ); ms_classInstances[ methodName ] = m_classInstance; } // Store Name Of Method And MethodName = methodName; m_method = classType.GetMethod( methodName ); IsSecret = isSecret; return; } /// Attempt to invoke the previously loaded method dynamically /// Parameters that should be passed to the method, or null if none /// A string return value from the method, or String.Empty if a null is returned or if the method returns void public string Invoke( params object[] parameters ) { string ret; try { object returnVal = m_method.Invoke( m_classInstance, parameters ); ret = ( returnVal != null ) ? returnVal.ToString() : String.Empty; } catch( Exception ex ) { ret = ex.Message; } return( ret ); } /// Provide a string representation of this object /// A string containing a representation of the object public override string ToString() { return ( String.Format( "{0}::{1}", m_classInstance, MethodName ) ); } #region IComparable Members /// Compare this object with another object instance /// The object to compare to /// The result of comparing the object's MethodName strings public int CompareTo( ExternalFuncObject other ) { return ( MethodName.CompareTo( other.MethodName ) ); } #endregion } #endregion #region Binding Object /// This object stores information on a bound key protected class BindingObject : IComparable< BindingObject > { /// Modifier keys for this key-binding [Flags] public enum EModifier { /// No modifier keys associated with this Binding None = 0, /// CTRL modifier key is associated with this Binding Ctrl = 1, /// SHIFT modifier key is associated with this Binding Shift = 2, /// ALT modifier key is associated with this Binding Alt = 4, } /// The command/function to execute when the bound key is hit public string _text; /// The key that the command/function is bound to public Keys _key; /// The modifier key for _key (Ctrl, Alt, Shift) - modifiers use the LEFT version of the key identifier public EModifier _modifierKeys; /// Gets the modifier key(s) in human readable form /// A string representing this binding's modifiers, or an empty string if none public string GetModifierString() { StringBuilder sb = new StringBuilder( 32 ); bool usesCtrl = ( ( _modifierKeys & EModifier.Ctrl ) == EModifier.Ctrl ); bool usesShift = ( ( _modifierKeys & EModifier.Shift ) == EModifier.Shift ); bool usesAlt = ( ( _modifierKeys & EModifier.Alt ) == EModifier.Alt ); bool first = false; if( usesCtrl ) { sb.Append( "CTRL" ); first = false; } if( usesAlt ) { if( !first ) { sb.Append( '+' ); } sb.Append( "ALT" ); } if( usesShift ) { if( !first ) { sb.Append( '+' ); } sb.Append( "SHIFT" ); } if( sb.Length > 0 ) { sb.Append( '+' ); } return( sb.ToString() ); } /// Convert this binding object into an informational string /// The informational string public override string ToString() { return ( String.Format( "<{0}{1}> -> {2}", GetModifierString(), _key, _text ) ); } #region IComparable Members /// Compare this object with another object instance /// The object to compare to /// Nothing - Not Implemented Yet public int CompareTo( BindingObject other ) { throw new NotImplementedException(); } #endregion } #endregion #region Fields #region Command Processing // -- Command Processing-Related Fields... /// Collection Of Command Objects. protected SortedDictionary ms_commands = new SortedDictionary(); /// Collection Of function Objects. protected SortedDictionary ms_functions = new SortedDictionary(); /// Collection Of External Functions. protected SortedDictionary ms_externalFunctions = new SortedDictionary(); /// Collection Of Binding Objects. protected List ms_bindings = new List( 8 ); /// Colleciton Of Partial Command Matches protected List ms_partialCmdMatches = new List( 8 ); /// Symbol table for the console variables protected Dictionary m_cVars = new Dictionary( 8 ); /// Match index for the last partial command protected int m_cmdMatchIndex = 0; /// The last command match found protected string m_lastCmdMatch = String.Empty; /// The command line itself protected string m_commandLine = String.Empty; /// Function-related functions are locked out protected bool m_functionsLocked = false; /// Binding-related functions are locked out protected bool m_bindingsLocked = false; /// External-Function-related functions are locked out protected bool m_exfunsLocked = false; #endregion #region Drawing and Graphics // -- Drawing/Graphics-Related Fields... /// Drawing object protected SpriteBatch m_spriteBatch; /// Font used for drawing the console's text protected SpriteFont m_consoleFont; /// Rectangle for the console area protected Rectangle m_consoleRect; /// Location of the command line protected Vector2 m_commandPos; /// Hight for the current console font protected Vector2 m_stringHeight; /// Offset for the command line protected float m_commandStringHeightOffset; /// Number of characters that can fit in the console area's width protected int m_screenCharWidth; /// Width of the console area protected int m_width; /// Height of the console area protected int m_height; /// Scale for drawing the console protected float m_scale = 1.0f; #endregion #region Scrolling and Logging // -- Scrolling-Related Fields... /// The number of lines that are visible in the console area protected int m_linesVisibleOnScreen; /// Used for drawing an indicator to show that lines are scrolled above protected bool m_linesAbove; /// Used for drawing an indicator to show that lines are scrolled below protected bool m_linesBelow; /// Where the log starts at (for scrolling) protected int m_logStart = 0; /// Specifies the limit of the log buffer protected int m_logLimit = 512; // -- Logging-Related Fields... /// The path to the log shadow (copy of all logged lines) protected string m_logShadowFilePath = String.Empty; /// Flag indicating that the log is being shadowed protected bool m_logShadowEnabled = false; /// The stream for shadowing the log to protected StreamWriter m_logShadowFile = null; /// Storage for the log protected static List m_log = new List( 256 ); #endregion #region Command History // Command History-Related Fields /// Specifies the limit of the command history protected int m_cmdHistoryLimit = 512; /// Collection of command history protected static List m_cmdHistory = new List( 128 ); /// Current index in the command history protected int m_cmdHistoryIndex = 0; /// Scanning (up/down) index in of the command history protected int m_cmdHistoryScanIndex = 0; #endregion #region Input Processing // Input related fields /// Time for key repeat protected DateTime m_keyRepeatTime = DateTime.MinValue; /// Character translation for ALL keyboard keys - contains an element for all values in the Keys enumeration protected List m_xlateAllKeys; /// Character translation for ALL keyboard keys, shifted - contains an element for all values in the Keys enumeration protected List m_xlateAllKeysShifted; #endregion #endregion #region Events /// Event fired right before handling/execution of a command. Allows interception of normal command processing. public event EventHandler PreCommandExecutedEvent; /// Event fired when a console variable has been created/modified public event EventHandler CVarModifiedEvent; #endregion #region Properties /// Interface to the keyboard used for console input public IConsoleKeyboard Keyboard { get; protected set; } /// Image/Texture used to fade the text underneath the console's text area public Texture2D FadeImage { get; set; } /// The color used to produce the fade public Color FadeColor { get; set; } /// Drawing scale for the console public float Scale { get { return m_scale; } set { m_scale = value; m_stringHeight = Vector2.Zero; } } /// Indicates if the console is active or not public static bool Active { get; set; } /// The prompt shown for the command line public string Prompt { get; set; } /// The color used to display entered commands public Color CommandColor { get; set; } /// The color used to display error messages public Color ErrorColor { get; set; } /// The color used to display output from executed commands public Color OutputColor { get; set; } /// The color used to display "normal" output messages public Color NormalColor { get; set; } /// The font used for text display public SpriteFont Font { get { return( m_consoleFont ); } set { m_consoleFont = value; m_stringHeight = Vector2.Zero; } } /// Sets/Gets the height of the console window public int Height { get { return( m_height ); } set { m_height = value; m_stringHeight = Vector2.Zero; } } /// Sets/Gets the width of the console window public int Width { get { return( m_width ); } set { m_width = value; m_stringHeight = Vector2.Zero; } } #endregion #region Initialization /// Constructor for this base class /// The Game object for the owning/managing game public CommandConsoleBase( Game game ) : this( game, null ) { return; } /// Constructor for this base class /// The Game object for the owning/managing game /// The font that the console should use to draw its text public CommandConsoleBase( Game game, SpriteFont fontToUse ) : base( game ) { m_consoleFont = fontToUse; Prompt = "_>"; CommandColor = Color.SteelBlue; ErrorColor = Color.Firebrick; OutputColor = Color.Yellow; NormalColor = Color.Silver; return; } /// Primary initialization function public override void Initialize() { base.Initialize(); // OK - This Seems Very Heavyweight, And It Kinda Is. But It Allows For // Easy Mapping Of A "Keys" Value To Any String, Which Makes It Possible // To Create Single Key Macros, Like Binding On Steroids, And Also Makes // It Easy To Handle Non-US Or Gaming Keyboards. // The Format Of The Lines Below Is A Comment Line That Identifies The Array // Indices, Which Correspond To Values In The "Keys" Enumeration, And Then // The Actual Values For Those Indices. m_xlateAllKeys = new List( new[] { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 " ", "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 48 49 50 51 52 53 54 55 56 57 58 59 60 61 63 63 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "" , "" , "" , "" , "" , "" , // 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 "" , "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", // 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "" , "" , "" , "" , "" , // 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "+", ",", "-", ".", "/", // 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 144 145 146 147 148 149 150 151 152 153 154 155 156 157 159 159 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , ";", "=", ",", "-", ".", "/", // 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 "", "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "[", "\\","]", "'", "8", // 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 "" , "" ,"|", "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , } ); // Create Collection Of Characters (Strings) For All Possible SHIFTED Key Values... m_xlateAllKeysShifted = new List( new[] { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 " ", "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 48 49 50 51 52 53 54 55 56 57 58 59 60 61 63 63 ")", "!", "@", "#", "$", "%", "^", "&", "*", "(", "" , "" , "" , "" , "" , "" , // 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 "" , "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", // 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "" , "" , "" , "" , "" , // 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "+", ",", "-", ".", "/", // 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 144 145 146 147 148 149 150 151 152 153 154 155 156 157 159 159 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , ":", "+", "<", "_", ">", "?", // 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 "", "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "{", "|", "}","\"", "*", // 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 "" , "" ,"\\", "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , // 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , } ); return; } #endregion #region Graphics Content /// Load content for this component /// The ContentManager that should be used public virtual void LoadContent( ContentManager content ) { m_spriteBatch = new SpriteBatch( GraphicsDevice ); m_width = GraphicsDevice.Viewport.Width; m_height = ( GraphicsDevice.Viewport.Height / 3 ); InitializeCommands(); base.LoadContent(); return; } #endregion #region Update and Draw /// /// Recalculates measurements required for text display and wrapping. Sets the m_consoleRect, /// m_commandPos and m_linesVisibleOnScreen fields. /// protected virtual void RecalculateHeightSettings() { m_consoleRect = new Rectangle( 0, 0, m_width, m_height ); m_commandPos = new Vector2( 10.0f, m_height - m_commandStringHeightOffset ); m_linesVisibleOnScreen = ( (int)( m_height / m_stringHeight.Y ) - 1 ); return; } /// Update this component /// Game's time public override void Update( GameTime gameTime ) { CheckInput(); base.Update( gameTime ); } /// Draw the console /// Game's time public override void Draw( GameTime gameTime ) { m_linesAbove = m_linesBelow = false; if( Active == true ) { m_spriteBatch.Begin( SpriteSortMode.Deferred, BlendState.AlphaBlend ); if( FadeImage != null ) { // Fade The Console Area With The Specified Texture/Image... m_spriteBatch.Draw( FadeImage, m_consoleRect, FadeColor ); } // // Cache Pre-Calculatable Values... // if( m_stringHeight == Vector2.Zero ) { m_stringHeight = Vector2.Multiply( m_consoleFont.MeasureString( Prompt ), Scale ); m_screenCharWidth = (int)( ( m_width / m_stringHeight.X ) * 2 ); m_commandStringHeightOffset = ( ( m_stringHeight.Y * 2.0f ) - 4.0f ); RecalculateHeightSettings(); } // Draw command string m_spriteBatch.DrawString( m_consoleFont, Prompt + m_commandLine, m_commandPos, Color.Yellow, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0.0f ); // Draw log Vector2 linePos = new Vector2( m_consoleRect.Left + 10.0f, ( m_commandPos.Y - m_stringHeight.Y ) ); int endLine = Math.Max( 0, ( ( m_log.Count - m_logStart ) - 1 ) ); if( m_logStart != 0 ) { m_linesBelow = true; } for( int i = endLine; i >= 0; i-- ) { if( linePos.Y <= m_consoleRect.Top ) { m_linesAbove = true; break; } String line = m_log[ i ]; if( line.Length == 0 ) { continue; } Char firstChar = line[ 0 ]; if( firstChar <= '\x03' ) { Color outColor = OutputColor; if( firstChar == '\x01' ) { outColor = CommandColor; } else if( firstChar == '\x02' ) { outColor = ErrorColor; } else if( firstChar == '\x03' ) { outColor = OutputColor; } m_spriteBatch.DrawString( m_consoleFont, line.Substring( 1 ), linePos, outColor, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0.0f ); } else { m_spriteBatch.DrawString( m_consoleFont, line, linePos, NormalColor, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0.0f ); } linePos.Y -= m_stringHeight.Y; } if( m_linesAbove ) { Vector2 starPos = new Vector2( m_width - 10.0f, m_consoleRect.Top ); m_spriteBatch.DrawString( m_consoleFont, "^", starPos, ErrorColor, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0.0f ); } if( m_linesBelow ) { Vector2 starPos = new Vector2( m_width - 10.0f, ( m_commandPos.Y - m_stringHeight.Y ) ); m_spriteBatch.DrawString( m_consoleFont, "V", starPos, ErrorColor, 0.0f, Vector2.Zero, Scale, SpriteEffects.None, 0.0f ); } m_spriteBatch.End(); } base.Draw( gameTime ); return; } #endregion #region Input Handling /// Process a keyboard key. Assumes that the Keyboard property is not null! /// The key to process protected virtual void ProcessKey( Keys key ) { // Space - Special Handling To Eliminate Leading Whitespace if( ( key == Keys.Space ) && ( !String.IsNullOrWhiteSpace( m_commandLine ) ) ) { m_commandLine += " "; ms_partialCmdMatches.Clear(); m_lastCmdMatch = String.Empty; } // Command History... else if( key == Keys.Up ) { if( m_cmdHistory.Count == 0 ) { return; } m_cmdHistoryScanIndex--; if( m_cmdHistoryScanIndex < 0 ) { m_cmdHistoryScanIndex = 0; } m_commandLine = m_cmdHistory[ m_cmdHistoryScanIndex ]; ms_partialCmdMatches.Clear(); } else if( key == Keys.Down ) { if( m_cmdHistory.Count == 0 ) { return; } m_cmdHistoryScanIndex++; if( m_cmdHistoryScanIndex >= m_cmdHistory.Count ) { m_cmdHistoryScanIndex = ( m_cmdHistory.Count - 1 ); } m_commandLine = m_cmdHistory[ m_cmdHistoryScanIndex ]; ms_partialCmdMatches.Clear(); } // Log Scrolling... else if( ( key == Keys.PageUp ) && ( m_linesVisibleOnScreen < m_log.Count ) ) { m_logStart += ( m_linesVisibleOnScreen - 1 ); if( m_logStart >= ( m_log.Count - m_linesVisibleOnScreen ) ) { m_logStart = ( m_log.Count - m_linesVisibleOnScreen + 1 ); } } else if( ( key == Keys.PageDown ) && ( m_linesVisibleOnScreen < m_log.Count ) ) { m_logStart -= ( m_linesVisibleOnScreen - 1 ); if( m_logStart < 0 ) { m_logStart = 0; } } // Check input for Escape else if( key == Keys.Escape ) { m_commandLine = String.Empty; ms_partialCmdMatches.Clear(); m_lastCmdMatch = String.Empty; } // Check input for Backspace else if( ( key == Keys.Back ) && ( !String.IsNullOrEmpty( m_commandLine ) ) && ( m_commandLine != Prompt ) ) { m_commandLine = m_commandLine.Remove( m_commandLine.Length - 1, 1 ); ms_partialCmdMatches.Clear(); m_lastCmdMatch = String.Empty; } // Check input for Tab else if( ( key == Keys.Tab ) && ( !String.IsNullOrEmpty( m_commandLine ) ) && ( m_commandLine != Prompt ) ) { // Rebuild Partial Command Matches If List Is Empty/Reset if( ms_partialCmdMatches.Count == 0 ) { BuildCommandMatches( m_commandLine ); m_cmdMatchIndex = 0; } // If We Have Partial Matches, Circularly Iterate Over Them And Show Them if( ms_partialCmdMatches.Count > 0 ) { if( m_cmdMatchIndex >= ms_partialCmdMatches.Count ) { m_cmdMatchIndex = 0; } m_commandLine = ms_partialCmdMatches[ m_cmdMatchIndex++ ]; } } // Enter - Commit Command Line else if( ( key == Keys.Enter ) && ( !String.IsNullOrWhiteSpace( m_commandLine ) ) ) { AddToLog( "\x01" + m_commandLine ); m_logStart = 0; ExecuteCommandLine( true ); m_commandLine = String.Empty; } // Any Other Key, Process As Character (Even If Not A Real Character Key) else { // Handle Shifted Chars if( ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.LeftShift ) ) || ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.RightShift ) ) ) { m_commandLine += m_xlateAllKeysShifted[ (int)key ]; } else { m_commandLine += m_xlateAllKeys[ (int)key ]; } ms_partialCmdMatches.Clear(); m_lastCmdMatch = String.Empty; } return; } /// Check input from the provided keyboard interface protected virtual void CheckInput() { //InputManager im = InputManager.Instance; // If No Interface To The Keyboard, Stop Here if( Keyboard == null ) { return; } // Toggle the console menu on or off if( Keyboard.NewlyPressedKeys.Contains( Keys.OemTilde ) ) { Active = !Active; return; } // Only Check Bindings If The Console Is Not Active if( Active == false ) { CheckBindings(); return; } if( Keyboard.HeldKeys.Count > 0 ) { if( DateTime.Now > m_keyRepeatTime ) { ProcessKey( Keyboard.HeldKeys[ 0 ] ); m_keyRepeatTime = DateTime.Now.AddMilliseconds( 100 ); } } if( Keyboard.NewlyPressedKeys.Count > 0 ) { ProcessKey( Keyboard.NewlyPressedKeys[ 0 ] ); m_keyRepeatTime = DateTime.Now.AddMilliseconds( 250 ); } return; } /// Get binding information from a "binding string" input by the user /// The binding string to parse /// ref - the modifier keys for the binding /// ref - the key for the binding /// true if the binding string was understood, false if not bool GetKeyBindingInfo( string bindString, ref BindingObject.EModifier bindModifiers, ref Keys bindKey ) { bindString = bindString.ToUpper(); if( bindString.Length == 1 ) { char keyChar = ( bindString.Trim().ToUpper()[ 0 ] ); int keyOffset = keyChar - 'A'; bindKey = ( Keys.A + keyOffset ); // Default To ALT Binding... // bindModifiers = BindingObject.EModifier.Alt; if( ( bindKey >= Keys.A ) && ( bindKey <= Keys.Z ) ) { return( true ); } else { return( false ); } } string[] keyTokens = bindString.Split( '+' ); bool parsed = false; foreach( string ktoken in keyTokens ) { if( ktoken == "ALT" ) { bindModifiers |= BindingObject.EModifier.Alt; continue; } if( ktoken == "CTRL" ) { bindModifiers |= BindingObject.EModifier.Ctrl; continue; } if( ktoken == "SHIFT" ) { bindModifiers |= BindingObject.EModifier.Shift; continue; } if( ktoken.Length == 1 ) { int keyOffset = ktoken[ 0 ] - 'A'; bindKey = ( Keys.A + keyOffset ); if( ( bindKey >= Keys.A ) && ( bindKey <= Keys.B ) ) { parsed = true; } else { parsed = false; } break; } else if( ( ( ktoken.Length == 2 ) || ( ktoken.Length == 3 ) ) && ( ktoken[ 0 ] == 'F' ) ) { // F-Key Binding... int fNumber = int.Parse( ktoken.Substring( 1 ) ); Keys fKey = ( Keys.Divide + fNumber ); if( ( fKey >= Keys.F1 ) && ( fKey <= Keys.F24 ) ) { bindKey = fKey; parsed = true; } else { parsed = false; } break; } else { break; } } return ( parsed ); } /// Check for bound keys - bound keys use the <ALT> modifier protected virtual void CheckBindings() { // Stop Here If No Bindings Are Set, Or No Interface To The Keyboard if( ( ms_bindings.Count == 0 ) || ( Keyboard == null ) ) { return; } //InputManager im = InputManager.Instance; BindingObject.EModifier modifierKeys = BindingObject.EModifier.None; bool altDown = ( ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.LeftAlt ) ) || ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.RightAlt ) ) ); bool ctrlDown = ( ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.LeftControl ) ) || ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.RightControl ) ) ); bool shiftDown = ( ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.LeftShift ) ) || ( Keyboard.CurrentKeyboardState.IsKeyDown( Keys.RightShift ) ) ); if( altDown ) modifierKeys |= BindingObject.EModifier.Alt; if( ctrlDown ) modifierKeys |= BindingObject.EModifier.Ctrl; if( shiftDown ) modifierKeys |= BindingObject.EModifier.Shift; foreach( BindingObject bs in ms_bindings ) { // See if the Binding's modifier keys are down, and its command key. If so, execute the command. if( ( ( bs._modifierKeys & modifierKeys ) == modifierKeys ) && ( Keyboard.NewlyPressedKeys.Contains( bs._key ) ) ) { m_commandLine = bs._text; ExecuteCommandLine( false ); m_commandLine = String.Empty; } } return; } /// Build command matches for the specified (sub)string. /// The partial command to try to match /// Nothing /// Resets the ms_partialCmdMatches collection protected virtual void BuildCommandMatches( string cmdMatch ) { ms_partialCmdMatches.Clear(); // Iterate Over Commands And Store Non-Secret Commands That Match Substring foreach( CmdObject cmd in ms_commands.Values ) { if( ( !cmd.IsSecret ) && ( cmd.Command.StartsWith( cmdMatch ) ) ) { ms_partialCmdMatches.Add( cmd.Command ); } } // Iterate Over Funcitons And Store Non-Secret Functions That Match Substring foreach( FuncObject func in ms_functions.Values ) { if( ( !func.IsSecret ) && ( func.Function.StartsWith( cmdMatch ) ) ) { ms_partialCmdMatches.Add( func.Function ); } } // Iterate Over External Funcitons And Store Non-Secret Functions That Match Substring foreach( ExternalFuncObject extFunc in ms_externalFunctions.Values ) { if( ( extFunc.IsSecret ) && ( extFunc.MethodName.StartsWith( cmdMatch ) ) ) { ms_partialCmdMatches.Add( extFunc.MethodName ); } } return; } #endregion #region Log Output /// Add executed command output to the log. /// The output text to add public void AddOutputToLog( string text ) { if( String.IsNullOrEmpty( text ) ) { return; } AddToLog( "\x03" + DateTime.Now.ToString( "hh:mm:ss.fff" ) + "-" + text ); return; } /// Add error output to the log. /// The error text to add public void AddErrorToLog( string text ) { if( String.IsNullOrEmpty( text ) ) { return; } AddToLog( "\x02" + DateTime.Now.ToString( "hh:mm:ss.fff" ) + "-" + text ); return; } /// Add executed command to the log. /// The command text to add public void AddCommandToLog( string text ) { if( String.IsNullOrEmpty( text ) ) { return; } AddToLog( "\x01" + DateTime.Now.ToString( "hh:mm:ss.fff" ) + "-" + text ); return; } /// Method to add generic output text to the log /// The text to add public void AddToLog( string text ) { if( String.IsNullOrEmpty( text ) ) { return; } if( m_logShadowEnabled ) { m_logShadowFile.WriteLine( text ); } lock( m_log ) { if( ( m_screenCharWidth == 0 ) || ( text.Length < m_screenCharWidth ) ) { m_log.Add( text ); return; } List lines = BreakTextIntoList( text, m_consoleFont, ( m_consoleRect.Width - 10 ), Scale ); foreach( String line in lines ) { m_log.Add( line ); } if( m_log.Count > m_logLimit ) { int remove = Math.Max( 32, ( m_log.Count - m_logLimit ) ); remove--; m_log.RemoveRange( 0, remove ); } } return; } // JRT Only Evaluate This Once... private static readonly char[] DELIMITERS = " .:,/\\-+=_[]{}()?!><|".ToCharArray(); /// /// Break text up into separate lines to make it fit. /// (Originally From MS Provided Code - RBG Starter Kit/Template - Text Helper Methods) /// /// The text to be broken up. /// The font used to measure the width of the text. /// The maximum width of each line, in pixels. /// Scale that should be used for the lines. protected static List BreakTextIntoList( string text, SpriteFont font, int rowWidth, float scale ) { // check parameters if( String.IsNullOrEmpty( text ) ) { return( new List( new[] { String.Empty } ) ); } if( font == null ) { throw new ArgumentNullException( "font" ); } if( rowWidth <= 0 ) { throw new ArgumentOutOfRangeException( "rowWidth" ); } // create the list List lines = new List( text.Length / rowWidth ); // check for trivial text if( String.IsNullOrEmpty( text ) ) { lines.Add( String.Empty ); return lines; } char cLead = text[ 0 ]; // // JRT Q&D Way To Get Rid Of The Coloring Characters - They Are Not Written // Out To The Screen, Anyway... // if( ( cLead == '\x01' ) || ( cLead == '\x02' ) || ( cLead == '\x03' ) ) { text = text.Substring( 1 ); } else { cLead = '\x00'; } // check for text that fits on a single line if( ( font.MeasureString( text ).X * scale ) <= rowWidth ) { if( cLead != '\x00' ) { text = text.Insert( 0, cLead.ToString() ); } return lines; } StringBuilder sb = new StringBuilder( 256 ); int textIndex = 0; int currentDelimiterIndex = 0; // JRT This Loop Will Cause The Occasional Leading Delimiter Character If The // Line Break Occurs At A Place Where To Delimiters Are Next To Each // Other, But I Consider This An Acceptable Tradeoff. // // Lines That Cannot Be Broken-Up Will Wrap, But The Wrapping Will Not // Be Optimal. while( textIndex < text.Length ) { String appendedString; int lastLen = textIndex; currentDelimiterIndex = text.IndexOfAny( DELIMITERS, textIndex ); if( currentDelimiterIndex == -1 ) { appendedString = text.Substring( textIndex ); } else { appendedString = text.Substring( textIndex, ( currentDelimiterIndex - textIndex + 1 ) ); } sb.Append( appendedString ); if( ( font.MeasureString( sb.ToString() ).X * scale ) > rowWidth ) { sb.Remove( ( sb.Length - appendedString.Length ), appendedString.Length ); if( cLead != '\x00' ) { sb.Insert( 0, cLead ); } lines.Add( sb.ToString() ); sb.Length = 0; text = text.Substring( textIndex ); textIndex = 0; } else { textIndex += appendedString.Length; } } if( sb.Length > 0 ) { lines.Add( sb.ToString() ); } return( lines ); } #endregion #region Built-In Commands /// Sets up the standard/built-in commands. protected void InitializeStandardCommands() { CmdObject cmdStruct = new CmdObject(); cmdStruct.Command = "help"; cmdStruct.CommandHelp = "show game console help, or help for a specific command"; cmdStruct.CommandHelpDetailed = "help [command]"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Help ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "quit"; cmdStruct.CommandHelp = "immediately quit the game"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Quit ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "close"; cmdStruct.CommandHelp = "close the command console."; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Close ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "consoleheight"; cmdStruct.MinParameters = 0; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "set the height of the console window in pixels"; cmdStruct.CommandHelpDetailed = "consoleheight "; cmdStruct.CmdEvent +=new Action(CommandConsoleBase_ConsoleHeight); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "consolescale"; cmdStruct.MinParameters = 0; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "set the scale of the console window (1.0=100%, 0.75=75%, 0.5=50%, etc.)"; cmdStruct.CommandHelpDetailed = "consolescale"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ConsoleScale ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "cvar"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 3; cmdStruct.CommandHelp = "add a new console variable (cvar), or modify an existing one"; cmdStruct.CommandHelpDetailed = "cvar [Type] -- examples: cvar myVar MyValue, cvar myInt Int32 42"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_CVar ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "cvars"; cmdStruct.MinParameters = 0; cmdStruct.MaxParameters = 0; cmdStruct.CommandHelp = "view the available console variables (cvars)"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_CVars ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "gc"; cmdStruct.CommandHelp = "immediately perform full garbage collection"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_GC ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "shadowlog"; cmdStruct.MinParameters = 0; cmdStruct.MaxParameters = 2; cmdStruct.CommandHelp = "shadows the log to an external file, optionally truncating the external file first"; cmdStruct.CommandHelpDetailed = "shadowlog "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ShadowLog ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "noshadowlog"; cmdStruct.CommandHelp = "turns off shadowing of the log to an external file"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_NoShadowLog ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "clear"; cmdStruct.CommandHelp = "clears the console log"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Clear ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "loglimit"; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "sets the console log (buffer) limit"; cmdStruct.CommandHelpDetailed = "loglimit "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_LogLimit ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "memoryinfo"; cmdStruct.CommandHelp = "display memory info"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_MemInfo ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exfunc"; cmdStruct.CommandHelp = "load external function from an assembly"; cmdStruct.CommandHelpDetailed = "exfunc "; cmdStruct.MinParameters = 3; cmdStruct.MaxParameters = 3; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ExFunc ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "systeminfo"; cmdStruct.CommandHelp = "display system info"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_SystemInfo ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "sleep"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "eause execution of the command console"; cmdStruct.CommandHelpDetailed = "sleep "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Sleep ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exec"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "execute contents of a file as commands in this console"; cmdStruct.CommandHelpDetailed = "exec "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Exec ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "savelog"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "save the console log to a file"; cmdStruct.CommandHelpDetailed = "savelog "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_SaveLog ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "functions"; cmdStruct.CommandHelp = "display all current functions"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Functions ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "nofunctions"; cmdStruct.CommandHelp = "disable the ability to add/remove functions"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_NoFunctions ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exfuncs"; cmdStruct.CommandHelp = "display all current external functions"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ExFuncs ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "noexfuncs"; cmdStruct.CommandHelp = "disable the ability to add/remove exfuncs"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_NoExFunctions ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "bindings"; cmdStruct.CommandHelp = "display all current key bindings"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Bindings ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "nobindings"; cmdStruct.CommandHelp = "disable the ability to add/remove bindings"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_NoBindings ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "resetbindings"; cmdStruct.CommandHelp = "clear all key bindings"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ResetBindings ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "resetexfuncs"; cmdStruct.CommandHelp = "clear all external functions"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ResetExFuncs ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "resetfunctions"; cmdStruct.CommandHelp = "clear all functions"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ResetFunctions); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "bind"; cmdStruct.MinParameters = 2; cmdStruct.MaxParameters = 0; cmdStruct.CommandHelp = "bind an key to a command or function"; cmdStruct.CommandHelpDetailed = "bind -- examples: 'bind ALT+G gc', 'bind CTRL+SHIFT+Q quit'"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Bind ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "unbind"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "remove a previous key binding"; cmdStruct.CommandHelpDetailed = "unbind "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_UnBind ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "function"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 0; cmdStruct.CommandHelp = "create a new function"; cmdStruct.CommandHelpDetailed = "function ;;<...>"; cmdStruct.CmdEvent += new Action( CommandConsoleBase_Function ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exportfunctions"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "export functions to a file that can be EXEC-ed"; cmdStruct.CommandHelpDetailed = "exportfunctions "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ExportFunctions ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exportexfuncs"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "export exfuncs to a file that can be EXEC-ed"; cmdStruct.CommandHelpDetailed = "exportexfuncs "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ExportExFuncs ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exportbindings"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "export binding information to a file that can be EXEC-ed"; cmdStruct.CommandHelpDetailed = "exportbindings "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ExportBindings ); ms_commands[ cmdStruct.Command ] = cmdStruct; cmdStruct = new CmdObject(); cmdStruct.Command = "exportstate"; cmdStruct.MinParameters = 1; cmdStruct.MaxParameters = 1; cmdStruct.CommandHelp = "export the console's complete state information to a file that can be EXEC-ed, so that the state can be restored later"; cmdStruct.CommandHelpDetailed = "exportstate "; cmdStruct.CmdEvent += new Action( CommandConsoleBase_ExportState ); ms_commands[ cmdStruct.Command ] = cmdStruct; return; } /// /// Overridable method used to add custom commands to the console. /// public virtual void InitializeCustomCommands() { return; } /// Initialize the Console's Commands -- is normally called via public void InitializeCommands() { InitializeStandardCommands(); InitializeCustomCommands(); return; } /// /// Add a command to the internal collection of commands. Throws an exception if duplicate identifiers are detected. /// /// Command object to add protected void AddCommand( CmdObject cmdStruct ) { cmdStruct.Command = cmdStruct.Command.Trim().ToLowerInvariant(); if( ms_commands.ContainsKey( cmdStruct.Command ) ) { throw( new InvalidOperationException( String.Format( "Command {0} already exists in the command list.", cmdStruct.Command ) ) ); } if( ms_functions.ContainsKey( cmdStruct.Command ) ) { throw ( new InvalidOperationException( String.Format( "Command {0} already exists as a function.", cmdStruct.Command ) ) ); } ms_commands[ cmdStruct.Command ] = cmdStruct; return; } /// /// Add a function to the internal collection of functions. Throws an exception if duplicate identifiers are detected. /// /// Function object to add protected void AddFunction( FuncObject fnStruct ) { if( ms_commands.ContainsKey( fnStruct.Function ) ) { throw ( new InvalidOperationException( String.Format( "Function {0} already exists as a command.", fnStruct.Function ) ) ); } if( ms_functions.ContainsKey( fnStruct.Function ) ) { throw ( new InvalidOperationException( String.Format( "Function {0} already exists in the function list.", fnStruct.Function ) ) ); } ms_functions[ fnStruct.Function ] = fnStruct; return; } #endregion #region Command Processing /// Execute the specified Function /// Function to execute /// Parameters for the function protected void ExecuteFunction( FuncObject fnStruct, string[] parameters ) { foreach( string line in fnStruct.FunctionImpl ) { m_commandLine = line; // If Function Parameters Exist, And Function Takes Parameters if( ( parameters != null ) && ( m_commandLine.IndexOf( '%' ) != -1 ) ) { int count = 0; // Build The Resulting Command Line foreach( string param in parameters ) { m_commandLine = m_commandLine.Replace( "%" + count, parameters[ count ] ); count++; } } AddToLog( "\x01-> " + m_commandLine ); ExecuteCommandLine( false ); m_commandLine = String.Empty; } return; } /// Evaluates and executes the command line stored in the "_command" field. public virtual void ExecuteCommandLine( bool addToHistory ) { string trimmedCmdLine = m_commandLine.Trim(); // Ignore Empty And Whitespace Command Lines... if( String.IsNullOrWhiteSpace( trimmedCmdLine ) ) { return; } // Ignore Comments... if( trimmedCmdLine[ 0 ] == '#' ) { return; } // Bang Indicates That This Line Should NOT Be Added To Command History if (trimmedCmdLine[0] == '!') { addToHistory = false; trimmedCmdLine = trimmedCmdLine.Substring(1); } else if( addToHistory ) { m_cmdHistory.Add(trimmedCmdLine); if( m_cmdHistory.Count > m_cmdHistoryLimit ) { m_cmdHistory.RemoveAt(m_cmdHistoryLimit); } m_cmdHistoryIndex = m_cmdHistory.Count; // m_cmdHistoryIndex = ( m_cmdHistory.Count - 1 ); m_cmdHistoryScanIndex = m_cmdHistoryIndex; } // If A PreCommand Execution Handling Delegate Has Been Set, // Execute The Delegate And See If The Command Was Handled // Externally. if( PreCommandExecutedEvent != null ) { CommandConsoleEventArgs ea = new CommandConsoleEventArgs( trimmedCmdLine ); try { PreCommandExecutedEvent( this, ea ); trimmedCmdLine = ea.CmdLine; } catch( Exception ex ) { AddErrorToLog( "** PreCommandExecuted delegate(s) thrown an exception: " + ex.Message ); } if( ea.Handled ) { return; } } try { CmdObject cs; String[] cmds = trimmedCmdLine.Split( ' ' ); String cmd = cmds[ 0 ].ToLowerInvariant(); bool found = false; // Commands Are more Common Then Functions, So Check them First. if( ms_commands.TryGetValue( cmd, out cs ) ) { found = ExecuteCommand( cs, cmds, cmd ); } // Next, Try External Functions... if( ( !found ) && ( ms_externalFunctions.Count > 0 ) ) { found = ExecuteExternalFunc( cmds, cmd ); } // Next, Try Internal Functions... if( ( !found ) && ( ms_functions.Count > 0 ) ) { found = ExecuteInternalFunc( cmds, cmd ); } // Lastly, See If A Console Variable... if( ( !found ) && ( m_cVars.Count > 0 ) ) { found = HandleCVar( cmds, cmd ); } // If STILL Not Found... if( !found ) { AddErrorToLog( "** Unknown command: " + trimmedCmdLine ); AddErrorToLog( "** type help for commands and usage." ); } } catch( Exception e ) { AddErrorToLog( String.Format( "Exception while processing \"{0}\" : {1}", trimmedCmdLine, e.Message ) ); } return; } /// Handle set/get cvar /// Command Line /// Pre-processed command /// true if command was handled by this function private bool HandleCVar( String[] cmds, String cmd ) { CVar cvar; bool found = false; lock( m_cVars ) { if( m_cVars.TryGetValue( cmd, out cvar ) ) { found = true; if( cmds.Length == 1 ) { AddOutputToLog( String.Format( "{0} ({1}) = {2}", cvar.Name, cvar.ValueType.ToString(), cvar.Value.ToString() ) ); } else { cvar.Value = GetObjFromString( cvar.ValueType, cmds[ 1 ] ); if( CVarModifiedEvent != null ) { CVarModifiedEvent.Invoke( this, new CVarModifiedEventArgs( cvar ) ); } } } } return( found ); } /// Execute an internal function /// Command Line /// Pre-processed command /// true if command was handled by this function private bool ExecuteInternalFunc( String[] cmds, String cmd ) { FuncObject fs; bool found = false; if( ms_functions.TryGetValue( cmd, out fs ) ) { string[] parameters = new string[ cmds.Length - 1 ]; for( int param = 1; param < cmds.Length; param++ ) { parameters[ param - 1 ] = cmds[ param ]; } ExecuteFunction( fs, parameters ); found = true; } return( found ); } /// Execute a previously loaded external function /// Command Line /// Pre-processed command /// true if command was handled by this function private bool ExecuteExternalFunc( String[] cmds, String cmd ) { ExternalFuncObject efs; bool found = false; // If Not Found, Try To Match Against External Functions if( ms_externalFunctions.TryGetValue( cmd, out efs ) ) { string[] parameters = new string[ cmds.Length - 1 ]; List cVarParams = new List( 2 ); bool doInvoke = true; for( int param = 1; param < cmds.Length; param++ ) { try { cVarParams.Add( m_cVars[ cmds[ param ].ToLowerInvariant() ] ); } catch( Exception ex ) { doInvoke = false; AddErrorToLog( "Exception while building parameters for external function: " + ex.Message ); } } if( doInvoke ) { string ret = efs.Invoke( cVarParams.ToArray() ); if( CVarModifiedEvent != null ) { foreach( CVar invokedVar in cVarParams ) { CVarModifiedEvent.Invoke( this, new CVarModifiedEventArgs( invokedVar ) ); } } if( String.IsNullOrWhiteSpace( ret ) == false ) { AddOutputToLog( ret ); } } found = true; } return found; } /// Execute a normal command /// Command Object /// Command Line /// Pre-processed command /// true if command was handled by this function private bool ExecuteCommand( CmdObject cs, String[] cmds, String cmd ) { int parameters = ( cmds.Length - 1 ); // Validate the parameter counts if( ( cs.MinParameters != 0 ) && ( parameters < cs.MinParameters ) ) { AddErrorToLog( "Not enough parameters specified for the " + cmd + " command." ); if( !String.IsNullOrEmpty( cs.CommandHelpDetailed ) ) { AddToLog( cs.CommandHelpDetailed ); } } else if( ( cs.MaxParameters != 0 ) && ( parameters > cs.MaxParameters ) ) { AddErrorToLog( "Too many parameters specified for the " + cmd + " command." ); if( !String.IsNullOrEmpty( cs.CommandHelpDetailed ) ) { AddToLog( cs.CommandHelpDetailed ); } } else { // Fire The Delegate! cs.TriggerEvent( cmds ); } return( true ); } #endregion #region Parsing Helpers /// Try to extract a double/Double value from the string /// Value to parse /// out - receives the value parsed from the string /// Error msg to log if parsing fails /// True if parsed successfully, false otherwise protected bool TryParseDouble( string value, out double parsedValue, string errorIfParsingFails ) { if( !Double.TryParse( value, out parsedValue ) ) { AddErrorToLog( errorIfParsingFails ); return ( false ); } return ( true ); } /// Try to extract a float/Single value from the string /// Value to parse /// out - receives the value parsed from the string /// Error msg to log if parsing fails /// True if parsed successfully, false otherwise protected bool TryParseFloat( string value, out float parsedValue, string errorIfParsingFails ) { if( !Single.TryParse( value, out parsedValue ) ) { AddErrorToLog( errorIfParsingFails ); return ( false ); } return ( true ); } /// Try to extract an int/Int32 value from the string /// Value to parse /// out - receives the value parsed from the string /// Error msg to log if parsing fails /// True if parsed successfully, false otherwise protected bool TryParseInt( string value, out int parsedValue, string errorIfParsingFails ) { if( !Int32.TryParse( value, out parsedValue ) ) { AddErrorToLog( errorIfParsingFails ); return ( false ); } return ( true ); } #endregion #region Standard Command Handlers void CommandConsoleBase_Help( string[] cmdLine ) { if( cmdLine.Length == 1 ) { StringBuilder sb = new StringBuilder( 512 ); bool first = true; sb.Append( "Commands: " ); foreach( CmdObject cs in ms_commands.Values ) { if( ( String.IsNullOrEmpty( cs.Command ) ) || ( cs.IsSecret ) ) { continue; } if( !first ) { sb.Append( ", " ); } first = false; sb.Append( cs.Command ); } AddToLog( sb.ToString() ); } else if( cmdLine.Length == 2 ) { CmdObject cs; String helpCommand = cmdLine[ 1 ]; bool found = false; if( ms_commands.TryGetValue( helpCommand, out cs ) ) { AddToLog( cs.Command + " - " + cs.CommandHelp ); if( !String.IsNullOrEmpty( cs.CommandHelpDetailed ) ) { AddToLog( cs.CommandHelpDetailed ); } found = true; } if( !found ) { AddErrorToLog( "Unknown command: " + helpCommand ); } } else { AddErrorToLog( "Too many parameters specified. Try help or help ." ); } return; } void CommandConsoleBase_Quit( string[] cmdLine ) { // Stop -- no questions asked! Game.Exit(); return; } void CommandConsoleBase_Exec( string[] cmdLine ) { string scriptFile = cmdLine[ 1 ]; if( !File.Exists( scriptFile ) ) { AddErrorToLog( "Unable to execute " + scriptFile + ", the file cannot be found." ); return; } string[] commandLines = File.ReadAllLines( cmdLine[ 1 ] ); if( commandLines.Length == 0 ) { AddErrorToLog( "File: " + scriptFile + " contains no lines." ); return; } AddOutputToLog( "<--- Starting execution of commands from: " + scriptFile + " at " + DateTime.Now.ToString( "hh:mm:ss.fff" ) ); bool bAddToHistory = commandLines[ 0 ].StartsWith( "!!" ); foreach( string cmd in commandLines ) { string cmdFixed = cmd.Trim(); AddToLog( "\x01-> " + cmd ); if( cmdFixed.Length == 0 ) { continue; } m_logStart = 0; m_commandLine = cmdFixed; ExecuteCommandLine( bAddToHistory ); m_commandLine = String.Empty; } AddOutputToLog( "<--- Ended execution of commands from: " + scriptFile + " at " + DateTime.Now.ToString( "hh:mm:ss.fff" ) ); return; } void CommandConsoleBase_SaveLog( string[] cmdLine ) { string[] lines = new String[ m_log.Count ]; m_log.CopyTo( lines ); File.WriteAllLines( cmdLine[ 1 ], lines ); AddOutputToLog( "<--- Log has been written to: " + cmdLine[ 1 ] ); return; } void CommandConsoleBase_GC( string[] cmdLine ) { AddOutputToLog( "Forcing Garbage Collection..." ); DateTime now = DateTime.Now; GC.Collect( 3, GCCollectionMode.Forced ); GC.Collect( 2, GCCollectionMode.Forced ); GC.Collect( 1, GCCollectionMode.Forced ); TimeSpan delta = DateTime.Now.Subtract( now ); AddOutputToLog( "Garbage Collection took ~" + delta.TotalMilliseconds + "ms" ); return; } void CommandConsoleBase_MemInfo( string[] cmdLine ) { AddOutputToLog( "WSMem: " + Environment.WorkingSet ); AddOutputToLog( "GCC1 : " + GC.CollectionCount( 0 ) ); AddOutputToLog( "GCC2 : " + GC.CollectionCount( 1 ) ); AddOutputToLog( "GCC3 : " + GC.CollectionCount( 2 ) ); AddOutputToLog( "Total: " + GC.GetTotalMemory( false ) ); return; } void CommandConsoleBase_ExFunc( string[] cmdLine ) { if( m_exfunsLocked ) { AddErrorToLog( "ExFunc-related actions have been disabled." ); return; } string assembly = cmdLine[ 1 ]; string className = cmdLine[ 2 ]; string funcName = cmdLine[ 3 ]; bool isSecret = false; isSecret = ( ( cmdLine.Length == 5 ) && ( cmdLine[ 4 ].ToLowerInvariant() == "secret" ) ); try { ExternalFuncObject efo = new ExternalFuncObject( assembly, className, funcName ); efo.CommandLine = String.Format( "exfunc {0} {1} {2}{3}", assembly, className, funcName, isSecret ? "secret" : String.Empty ); ms_externalFunctions.Add( funcName.ToLowerInvariant(), efo ); AddOutputToLog( String.Format( "Added external function {0}::{1}::{2}", assembly, className, funcName ) ); } catch( Exception ex ) { AddErrorToLog( String.Format( "Unable to load/add {0}::{1}::{2} = {3}", assembly, className, funcName, ex.Message ) ); } return; } void CommandConsoleBase_SystemInfo( string[] cmdLine ) { AddOutputToLog( "Name : " + Environment.MachineName ); AddOutputToLog( "OSVer : " + Environment.OSVersion ); AddOutputToLog( "64BitOS : " + Environment.Is64BitOperatingSystem ); AddOutputToLog( "64BitProcess : " + Environment.Is64BitProcess ); AddOutputToLog( "PageFile : " + Environment.SystemPageSize ); AddOutputToLog( "CPUs : " + Environment.ProcessorCount ); AddOutputToLog( "CLRVer : " + Environment.Version ); return; } /// Type converter helper - tries to convert the specified string to the specified type /// The target type to convert to /// The source string to try to convert /// A reference to the successfully converted object, or null if conversion failed public static object GetObjFromString( Type type, string mystring ) { var foo = TypeDescriptor.GetConverter( type ); // Try To Convert From String To The Target Type Using Both Culture-Sensitive // And The Culture-Insensitive Conversion. If Both Fail, Default To String // Type. try { return ( foo.ConvertFromString( mystring ) ); } catch( Exception ) { try { return ( foo.ConvertFromInvariantString( mystring ) ); } catch( Exception ) { // Just Eat This One } } // If Conversion Fails, Fall Back To String Type return ( mystring ); } void CommandConsoleBase_CVar( string[] cmdLine ) { CVar cvar = null; lock( m_cVars ) { string cvarName = cmdLine[ 1 ].ToLowerInvariant(); // Isn't this preventing us from modifying a cvar? // -- Nathan Adams [adamsna@datanethost.net] - 6/3/2012 if( m_cVars.TryGetValue( cvarName, out cvar ) ) { AddErrorToLog( "CVar " + cmdLine[ 1 ] + " already exists with a value of: " + cvar.Value ?? "(null" ); return; } if( cmdLine.Length == 4 ) { string typeName = cmdLine[ 2 ]; Type cvarType = Type.GetType( typeName, false ); if( cvarType == null ) { cvarType = Type.GetType( "System." + typeName, false ); } if( cvarType == null ) { AddErrorToLog( "Cannot resolve type " + typeName + " -- try a fully qualified type name" ); return; } object objValue = GetObjFromString( cvarType, cmdLine[ 3 ] ); cvar = new CVar( cvarName, objValue ); } else if( cmdLine.Length == 3 ) { cvar = new CVar( cvarName, cmdLine[ 2 ] ); } else { cvar = new CVar( cvarName ); } m_cVars[ cvarName ] = cvar; AddOutputToLog( "CVar " + cvarName + " has been added" ); } if( ( cvar != null ) && ( CVarModifiedEvent != null ) ) { CVarModifiedEvent.Invoke(this, new CVarModifiedEventArgs(cvar)); } return; } void CommandConsoleBase_CVars( string[] cmdLine ) { AddOutputToLog( "cvars:" ); lock( m_cVars ) { foreach( CVar cvar in m_cVars.Values ) { if( cvar.Value != null ) { AddOutputToLog( String.Format( "{0} ({1}) = {2}", cvar.Name, cvar.ValueType.ToString(), cvar.Value.ToString() ) ); } else { AddOutputToLog( cvar.Name + " (null) = null" ); } } } return; } void CommandConsoleBase_ConsoleScale( string[] cmdLine ) { if( cmdLine.Length < 2 ) { AddOutputToLog( "Current ConsoleScale is: " + Scale ); return; } float newScale; if( !TryParseFloat( cmdLine[ 1 ], out newScale, "Invalid scale - the specified value cannot be parsed" ) ) { return; } if( ( newScale < 0.10f ) || ( newScale > 4.0f ) ) { AddErrorToLog( "Invalid scale - must be between 0.10 (10%) and 4.0 (400%)" ); } else { Scale = newScale; m_stringHeight = Vector2.Zero; } return; } void CommandConsoleBase_ConsoleHeight( string[] cmdLine ) { if( cmdLine.Length < 2 ) { AddOutputToLog( "Current ConsoleHeight value is: " + m_height ); return; } int newHeight; if( !TryParseInt( cmdLine[ 1 ], out newHeight, "Invalid height - the specified value cannot be parsed" ) ) { return; } if( ( newHeight < 100 ) || ( newHeight > ( GraphicsDevice.Viewport.Height - 100 ) ) ) { AddErrorToLog( "Invalid height - must be between 100 and " + ( GraphicsDevice.Viewport.Height - 100 ) ); } else { m_height = newHeight; m_stringHeight = Vector2.Zero; } return; } void CommandConsoleBase_Sleep( string[] cmdLine ) { int sleepms; if( !TryParseInt( cmdLine[ 1 ], out sleepms, "Invalid sleep duration - the specified value cannot be parsed" ) ) { return; } Thread.Sleep( sleepms ); return; } void CommandConsoleBase_LogLimit( string[] cmdLine ) { if( cmdLine.Length < 2 ) { AddOutputToLog( "Current LogLimit value is: " + m_logLimit ); return; } int lines; if( !TryParseInt( cmdLine[ 1 ], out lines, "Invalid line count - the specified value cannot be parsed" ) ) { return; } if( ( lines < 32 ) || ( lines > 10240 ) ) { AddErrorToLog( "Invalid line count - must be between 32 and 10240" ); } else { m_logLimit = lines; } return; } void CommandConsoleBase_Close( string[] cmdLine ) { Active = false; return; } void CommandConsoleBase_ShadowLog( string[] cmdLine ) { if( cmdLine.Length == 1 ) { if( m_logShadowEnabled ) { AddOutputToLog( "Log shadowing is enabled, writing to: " + m_logShadowFilePath ); m_logShadowFile.Flush(); } else { AddOutputToLog( "Log shadowing is disabled." ); } return; } string filePath = cmdLine[ 1 ]; bool truncate = false; if( cmdLine.Length == 3 ) { if( ( bool.TryParse( cmdLine[ 2 ], out truncate ) ) && ( truncate ) ) { File.Delete( cmdLine[ 2 ] ); } } m_logShadowFile = new StreamWriter( filePath, !truncate, Encoding.ASCII ); m_logShadowFilePath = filePath; m_logShadowEnabled = true; AddOutputToLog( "Log shadowing has been enabled." ); return; } void CommandConsoleBase_NoShadowLog( string[] cmdLine ) { if( m_logShadowEnabled ) { m_logShadowFile.Close(); m_logShadowFile = null; m_logShadowEnabled = false; AddOutputToLog( "Log shadowing has been disabled." ); } else { AddOutputToLog( "Log shadowing was not enabled." ); } return; } void CommandConsoleBase_Clear( string[] cmdLine ) { m_log.Clear(); return; } void CommandConsoleBase_NoFunctions( string[] cmdLine ) { m_functionsLocked = true; return; } void CommandConsoleBase_NoBindings( string[] cmdLine ) { m_bindingsLocked = true; return; } void CommandConsoleBase_NoExFunctions( string[] cmdLine ) { m_exfunsLocked = true; return; } void CommandConsoleBase_Bindings( string[] cmdLine ) { if( ms_bindings.Count == 0 ) { AddOutputToLog( "No bindings have been set." ); return; } StringBuilder sb = new StringBuilder( 255 ); bool first = true; sb.Append( "Bindings: " ); foreach( BindingObject bs in ms_bindings ) { if( !first ) { sb.Append( ", " ); } first = false; sb.Append( bs.ToString() ); } AddToLog( sb.ToString() ); return; } void CommandConsoleBase_ResetBindings( string[] cmdLine ) { if( m_bindingsLocked ) { AddErrorToLog( "Binding-related actions have been disabled." ); return; } if( ms_bindings.Count == 0 ) { AddOutputToLog( "No bindings have been set." ); return; } ms_bindings.Clear(); AddOutputToLog( "All bindings have been cleared." ); return; } void CommandConsoleBase_ResetFunctions( string[] cmdLine ) { if( m_functionsLocked ) { AddErrorToLog( "Function-related actions have been disabled." ); return; } if( ms_functions.Count == 0 ) { AddOutputToLog( "No functions have been created." ); return; } ms_functions.Clear(); AddOutputToLog( "All functions have been cleared." ); return; } void CommandConsoleBase_ResetExFuncs( string[] cmdLine ) { if( m_exfunsLocked ) { AddErrorToLog( "ExFunc-related actions have been disabled." ); return; } if( ms_externalFunctions.Count == 0 ) { AddOutputToLog( "No external functions have been added." ); return; } ms_externalFunctions.Clear(); AddOutputToLog( "All external functions have been cleared." ); return; } void CommandConsoleBase_ExFuncs( string[] cmdLine ) { if( m_exfunsLocked ) { AddErrorToLog( "ExFunc-related actions have been disabled." ); return; } if( ms_externalFunctions.Count == 0 ) { AddOutputToLog( "No external functions have been added." ); return; } StringBuilder sb = new StringBuilder( 255 ); bool first = true; sb.Append( "ExFuncs: " ); foreach( ExternalFuncObject ef in ms_externalFunctions.Values ) { if( !first ) { sb.Append( ", " ); } first = false; sb.Append( ef.ToString() ); } AddToLog( sb.ToString() ); return; } void CommandConsoleBase_Functions( string[] cmdLine ) { if( ms_functions.Count == 0 ) { AddOutputToLog( "No functions have been created." ); return; } StringBuilder sb = new StringBuilder( 255 ); bool first = true; sb.Append( "Functions: " ); foreach( FuncObject fs in ms_functions.Values ) { if( !first ) { sb.Append( ", " ); } first = false; sb.Append( fs.ToString() ); } AddToLog( sb.ToString() ); return; } private List GetAllExFuncs() { List exFuncs = new List( ms_externalFunctions.Count ); foreach( ExternalFuncObject efo in ms_externalFunctions.Values ) { exFuncs.Add( efo.CommandLine ); } return( exFuncs ); } private List GetAllFunctions() { List functions = new List( ms_functions.Count ); foreach( FuncObject fs in ms_functions.Values ) { string fnText = "function " + fs.ToString().Replace( "-> ", String.Empty ); //foreach( string fnline in fs.FunctionImpl ) //{ // fnText += fnline; // fnText += ";"; //} //functions.Add( "function " + fs.Function + " " + fnText ); functions.Add( fnText ); } return( functions ); } private List< string > GetAllBindings() { List bindings = new List( ms_bindings.Count ); foreach( BindingObject bo in ms_bindings ) { bindings.Add( "bind " + bo.GetModifierString() + bo._key + " " + bo._text ); } return( bindings ); } void CommandConsoleBase_ExportBindings( string[] cmdLine ) { if( m_bindingsLocked ) { AddErrorToLog( "Binding-related actions have been disabled." ); return; } if( ms_bindings.Count == 0 ) { AddErrorToLog( "No bindings have been set." ); return; } string filePath = cmdLine[ 1 ]; File.WriteAllLines( filePath, GetAllBindings().ToArray() ); AddOutputToLog( "Bindings have been saved to: " + filePath ); return; } void CommandConsoleBase_ExportFunctions( string[] cmdLine ) { if( m_functionsLocked ) { AddErrorToLog( "Function-related actions have been disabled." ); return; } if( ms_functions.Count == 0 ) { AddErrorToLog( "No functions have been set." ); return; } string filePath = cmdLine[ 1 ]; File.WriteAllLines( filePath, GetAllFunctions().ToArray() ); AddOutputToLog( "Functions have been saved to: " + filePath ); return; } void CommandConsoleBase_ExportExFuncs( string[] cmdLine ) { if( m_exfunsLocked ) { AddErrorToLog( "ExFunc-related actions have been disabled." ); return; } if( ms_externalFunctions.Count == 0 ) { AddErrorToLog( "No ExFuncs have been set." ); return; } string filePath = cmdLine[ 1 ]; File.WriteAllLines( filePath, GetAllExFuncs().ToArray() ); AddOutputToLog( "ExFunc defs have been saved to: " + filePath ); return; } /// Special command handler for ExportState command /// The command line for the command /// This one is protected virtual so additional state can be stored if necessary by derived classes protected virtual void CommandConsoleBase_ExportState( string[] cmdLine ) { string filePath = cmdLine[ 1 ]; StreamWriter outFile = new StreamWriter( filePath, false, Encoding.ASCII ); if( ms_bindings.Count > 0 ) { lock( ms_bindings ) { List bindings = GetAllBindings(); foreach( string line in bindings ) { outFile.WriteLine( line ); } } } if( ms_functions.Count > 0 ) { lock( ms_functions ) { List functions = GetAllFunctions(); foreach( string line in functions ) { outFile.WriteLine( line ); } } } outFile.Write( "consoleheight " + m_height ); outFile.Write( "consolescale " + Scale ); outFile.Write( "loglimit " + m_logLimit ); outFile.Close(); outFile.Dispose(); AddOutputToLog( "State has been saved to: " + filePath ); return; } void CommandConsoleBase_UnBind( string[] cmdLine ) { string bindString = cmdLine[ 1 ]; Keys bindKey = Keys.None; BindingObject.EModifier bindKeyModifiers = BindingObject.EModifier.None; bool parsed = GetKeyBindingInfo( bindString, ref bindKeyModifiers, ref bindKey ); if( !parsed ) { AddErrorToLog( String.Format( "\"{0}\" was not understood as a proper binding string.", bindString ) ); return; } foreach( BindingObject bs in ms_bindings ) { if( ( bindKey == bs._key ) && ( bindKeyModifiers == bs._modifierKeys ) ) { ms_bindings.Remove( bs ); AddOutputToLog( bs.GetModifierString() + " has been unbound." ); return; } } AddErrorToLog( bindString + " is not bound." ); return; } void CommandConsoleBase_Bind( string[] cmdLine ) { if( m_bindingsLocked ) { AddErrorToLog( "Binding-related actions have been disabled." ); return; } string bindString = cmdLine[ 1 ]; BindingObject.EModifier bindKeyModifiers = BindingObject.EModifier.None; Keys bindKey = Keys.None; bool parsed = GetKeyBindingInfo( bindString, ref bindKeyModifiers, ref bindKey ); if( !parsed ) { AddErrorToLog( String.Format( "\"{0}\" was not understood as a proper binding string.", bindString ) ); return; } foreach( BindingObject bs in ms_bindings ) { if( ( bindKey == bs._key ) && ( bindKeyModifiers == bs._modifierKeys ) ) { AddErrorToLog( String.Format( "{0} is already bound.", bindString ) ); return; } } BindingObject newBS = new BindingObject(); string cmds = String.Empty; for( int cmd = 2; cmd < cmdLine.Length; cmd++ ) { cmds += cmdLine[ cmd ]; cmds += " "; } newBS._key = bindKey; newBS._modifierKeys = bindKeyModifiers; newBS._text = cmds; ms_bindings.Add( newBS ); return; } void CommandConsoleBase_Function( string[] cmdLine ) { if( m_functionsLocked ) { AddErrorToLog( "Function-related actions have been disabled." ); return; } string cmds = String.Empty; bool isSecret = false; // This Is A Bit Heavyweight, Building A command Line And Then // Breaking It Apart Again, But It Seemed Easier And Less // Complicated Than Trying To Do It All In One Shot // // Build Command Line Minus Command And Secret Option for( int cmd = 2; cmd < cmdLine.Length; cmd++ ) { if( ( cmd == 2 ) && ( cmdLine[ cmd ].ToLowerInvariant() == "secret" ) ) { isSecret = true; continue; } cmds += cmdLine[ cmd ]; cmds += " "; } if( String.IsNullOrWhiteSpace( cmds ) ) { AddErrorToLog( "Function contains no code to execute." ); return; } string[] ft = cmds.Split( ';' ); bool hasCode = false; // Reassemble Command Lines From Tokens for( int line = 0; line