diff --git a/axios/Axios_WP7.csproj b/axios/Axios_WP7.csproj index 70983a8..897dcf9 100644 --- a/axios/Axios_WP7.csproj +++ b/axios/Axios_WP7.csproj @@ -232,6 +232,8 @@ + + diff --git a/axios/Axios_Windows.csproj b/axios/Axios_Windows.csproj index 9f27321..195fc78 100644 --- a/axios/Axios_Windows.csproj +++ b/axios/Axios_Windows.csproj @@ -276,6 +276,8 @@ + + diff --git a/axios/Axios_Windows.csproj.user b/axios/Axios_Windows.csproj.user index 76fe5a5..566c009 100644 --- a/axios/Axios_Windows.csproj.user +++ b/axios/Axios_Windows.csproj.user @@ -1,6 +1,6 @@  - ProjectFiles + ShowAllFiles \ No newline at end of file diff --git a/axios/Axios_Xbox_360.csproj b/axios/Axios_Xbox_360.csproj index 4ccbed8..9ce7f6f 100644 --- a/axios/Axios_Xbox_360.csproj +++ b/axios/Axios_Xbox_360.csproj @@ -224,6 +224,8 @@ + + diff --git a/axios/XNACC/CommandConsoleBase.cs b/axios/XNACC/CommandConsoleBase.cs new file mode 100644 index 0000000..604b95e --- /dev/null +++ b/axios/XNACC/CommandConsoleBase.cs @@ -0,0 +1,2951 @@ +#region Using Statements +#if WINDOWS +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 JRTS.XNA.Console.BaseTypes; +using System.Diagnostics; +#endif +#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? +#if WINDOWS +/// Namespace that contains code related to the XNACC (CommandConsole) component +namespace JRTS.XNA.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 static SortedDictionary ms_commands = new SortedDictionary(); + /// Collection Of function Objects. + protected static SortedDictionary ms_functions = new SortedDictionary(); + /// Collection Of External Functions. + protected static SortedDictionary ms_externalFunctions = new SortedDictionary(); + /// Collection Of Binding Objects. + protected static List ms_bindings = new List( 8 ); + + /// Colleciton Of Partial Command Matches + protected static 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 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 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 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(); + + 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 Namespace that contains shared types related to the XNACC (CommandConsole) component +namespace JRTS.XNA.Console.BaseTypes +{ + #region IConsoleKeyboard + /// Basic keyboard/input functionality required by the CommandConsole(Base) class + public interface IConsoleKeyboard + { + /// The current state of the keyboard as of the last Update + KeyboardState CurrentKeyboardState + { + get; + } + /// Collection of keys that are newly pressed (i.e. not held) as of the last Update + IList NewlyPressedKeys + { + get; + } + /// Collection of keys that are being held fown (i.e. not newly pressed) as of the last Update + IList HeldKeys + { + get; + } + } + #endregion + + #region CVar Object + // JRT: OK - Why Is This In Its Own File? Because Of The Way The Type System Works In .NET. If You Create + // JRT: Two Completely Identical Types, But Put Them In Different Assemblies, They Are Considered To Be + // JRT: Two Completely DIFFERENT Types, Even Though They Are Compatible With One Another. By Putting + // JRT: The CVar Class Into A Separate Assembly, Both The CommandConsole And External Functions + // JRT: Reference The Same Type, Allowing Us To Pass CVars Back And Forth. + /// Object that wraps the functionality of a console variable -- defined separately because external functions will receive CVar instances + public class CVar + { + /// The storage for the Name property + protected string m_name = String.Empty; + /// The storage for the Value property + protected object m_value = null; + + /// The string name for this console variable + public string Name + { + get + { + return (m_name); + } + protected set + { + if (String.IsNullOrWhiteSpace(value)) + { + throw new ArgumentNullException("The name for a console variable cannot be null, empty, or whitespace"); + } + m_name = value; + } + } + /// The actual value for this console variable, as an object, or null + public object Value + { + get + { + return( m_value ); + } + set + { + // Set Value. Set Type To Type.Missing If Value Is null + m_value = value; + if (value == null) + { + ValueType = (Type)Type.Missing; + } + else + { + ValueType = value.GetType(); + } + } + } + /// The actual type of the contained variable, or Type.Missing for null values + public Type ValueType + { + get; + protected set; + } + /// Construct a console variable with a default value of null + /// The name for ths console variable + public CVar(string name ) + : this( name, null ) + { + return; + } + /// Construct a console variable with the specified name and value + /// The name for ths console variable + /// The value for this console variable + public CVar(string name, object value) + { + Name = name; + Value = value; + return; + } + /// Get a string representation of the object (not round-trippable!) + /// A string representation of this CVar + public override string ToString() + { + return( String.Format( "{0} ({1}) = {2}", Name, + ValueType.ToString(), + Value.ToString() ) ); + } + /// Hash code function - needed for the dictionary + /// The hash value of the Name of the cvar + override public int GetHashCode() + { + return(Name.GetHashCode() ); + } + } + #endregion +} +#endif \ No newline at end of file