axiosengine/axios/XNACC/CommandConsoleBase.cs
Nathan Adams 01748bc5f8 Fixing axios log flag detection
Adding dispose methods to AxiosFile objects
Adding extended log to AxiosLog
Fixing issue in CommandConsole where the first line would not be displayed
Adding commands to commandconsole
2015-01-02 20:14:38 -06:00

2953 lines
106 KiB
C#

#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 <ALT>-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 <ALT/SHIFT/CTRL> 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?
/// <summary>Namespace that contains code related to the XNACC (CommandConsole) component</summary>
namespace XNACC.Console
{
/// <summary>Base functionality of the XNACC (CommandConsole) component</summary>
public class CommandConsoleBase : DrawableGameComponent
{
#region EventArgs Object
/// <summary>EA object for the CVarModified event</summary>
public class CVarModifiedEventArgs : EventArgs
{
/// <summary>The actual CVar for this event</summary>
public CVar Value
{
get;
protected set;
}
/// <summary>Constructor for this object</summary>
/// <param name="value">The cvar that has been modified</param>
public CVarModifiedEventArgs(CVar value)
{
Value = value;
return;
}
}
/// <summary>EA object for handling console command execution</summary>
public class CommandConsoleEventArgs : EventArgs
{
/// <summary>The actual command line being executed</summary>
public string CmdLine
{
get;
set;
}
/// <summary>Flag indicating if the command has been handled by an event handler; if true, no further processing is done on the command</summary>
public bool Handled
{
get;
set;
}
/// <summary>Constructor for this object</summary>
/// <param name="cmdLine">The command line being processed</param>
public CommandConsoleEventArgs( string cmdLine )
{
CmdLine = cmdLine;
Handled = false;
return;
}
}
#endregion
#region Command Object
/// <summary>This object contains information on a single Command that the console understands.</summary>
protected class CmdObject : IComparable< CmdObject >
{
/// <summary>Default constructor for the Command Object</summary>
public CmdObject()
: this( String.Empty, String.Empty, null, false, String.Empty, 0, 0 )
{
return;
}
/// <summary>Constuct an instance of the Command Object</summary>
/// <param name="command">The command identifier (no whitespace, please)</param>
/// <param name="help">The (brief) help for this command</param>
/// <param name="cmdAction">The delegate method that fires when the command is encountered</param>
public CmdObject( string command, string help, Action<string []> cmdAction )
: this( command, help, cmdAction, false, String.Empty, 0, 0 )
{
return;
}
/// <summary>Constuct an instance of the Command Object</summary>
/// <param name="command">The command identifier (no whitespace, please)</param>
/// <param name="help">The (brief) help for this command</param>
/// <param name="cmdAction">The delegate method that fires when the command is encountered</param>
/// <param name="isSecret">Is this a secret command (not shown in the help information)</param>
public CmdObject( string command, string help, Action<string []> cmdAction, bool isSecret )
: this( command, help, cmdAction, isSecret, String.Empty, 0, 0 )
{
return;
}
/// <summary>Constuct an instance of the Command Object</summary>
/// <param name="command">The command identifier (no whitespace, please)</param>
/// <param name="help">The (brief) help for this command</param>
/// <param name="cmdAction">The delegate method that fires when the command is encountered</param>
/// <param name="isSecret">Is this a secret command (not shown in the help information)</param>
/// <param name="detailedHelp">The detailed help for this command</param>
public CmdObject( string command, string help, Action<string []> cmdAction, bool isSecret, string detailedHelp )
: this( command, help, cmdAction, isSecret, detailedHelp, 0, 0 )
{
return;
}
/// <summary>Constuct an instance of the Command Object</summary>
/// <param name="command">The command identifier (no whitespace, please)</param>
/// <param name="help">The (brief) help for this command</param>
/// <param name="cmdAction">The delegate method that fires when the command is encountered</param>
/// <param name="isSecret">Is this a secret command (not shown in the help information)</param>
/// <param name="detailedHelp">The detailed help for this command</param>
/// <param name="minParams">The minimum number of parameters for this command (0 == no minimum)</param>
/// <param name="maxParams">The maximum number of parameters for this command (0 == no maximum)</param>
public CmdObject( string command, string help, Action<string []> 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;
}
/// <summary>Required - The command (single word, no spaces, and all commands will be converted to lowercase)</summary>
public String Command { get; set; }
/// <summary>Optional - Summary help information that briefly describes the command</summary>
public String CommandHelp { get; set; }
/// <summary>Optional - Detailed help information that explains the command and its usage</summary>
public String CommandHelpDetailed { get; set; }
/// <summary>The minimum number of parameters the command expects (set to zero for no minimum)</summary>
public int MinParameters { get; set; }
/// <summary>The maximum number of parameters the command expects (set to zero for no maximum)</summary>
public int MaxParameters { get; set; }
/// <summary>Indicates if this command is secret -- if so, it will not appear when the user types
/// "help" and will not participate in command completion</summary>
public bool IsSecret { get; set; }
/// <summary>The event fired when the associated command is received</summary>
public event Action<string[]> CmdEvent;
/// <summary>This method fires the associated Delegate</summary>
/// <param name="cmdLine">The complete command line. Not just the command's parameters!</param>
/// <remarks>This method is public to allow for artificial triggering of commands.</remarks>
public void TriggerEvent( String[] cmdLine )
{
if( CmdEvent != null )
{
CmdEvent.Invoke( cmdLine );
}
}
#region IComparable<CmdObject> Members
/// <summary>Compare this object with another object instance</summary>
/// <param name="other">The object to compare to</param>
/// <returns>The result of comparing the object's Command strings</returns>
public int CompareTo( CmdObject other )
{
return( Command.CompareTo( other.Command ) );
}
#endregion
}
#endregion
#region Function Object
/// <summary>This object contains information on a custom function that the console understands.</summary>
protected class FuncObject : IComparable< FuncObject >
{
/// <summary>The function name</summary>
public String Function
{
get;
protected set;
}
/// <summary>The code to execute when the function executes</summary>
public IList<String> FunctionImpl
{
get;
protected set;
}
/// <summary>Constructor for the Function Object</summary>
/// <param name="funcName">The name of the Function</param>
/// <param name="funcImpl">The implementation of the Function</param>
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<string>( funcImpl );
return;
}
/// <summary>Indicates if this function is secret -- it will not appear when the user types "help"</summary>
public bool IsSecret
{
get;
set;
}
/// <summary>Provide a string representation of this object</summary>
/// <returns>A string containing a representation of the object</returns>
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<FuncObject> Members
/// <summary>Compare this object with another object instance</summary>
/// <param name="other">The object to compare to</param>
/// <returns>The result of comparing the object's Function strings</returns>
public int CompareTo( FuncObject other )
{
return( Function.CompareTo( other.Function ) );
}
#endregion
}
#endregion
#region External Function Object
/// <summary>This object contains information on an external function (implemented in a .NET assembly) that the console understands.</summary>
protected class ExternalFuncObject : IComparable<ExternalFuncObject>
{
/// <summary>Collection Of Known Class Instances</summary>
static protected Dictionary< string, object > ms_classInstances = new Dictionary<string,object>( 8 );
/// <summary>The method name</summary>
public String MethodName
{
get;
set;
}
/// <summary>Is this external function secret?</summary>
public bool IsSecret
{
get;
protected set;
}
/// <summary>Command line used to create the function</summary>
public String CommandLine
{
get;
set;
}
/// <summary>The actual method metadata</summary>
protected MethodInfo m_method;
/// <summary>The actual class instance</summary>
protected object m_classInstance;
/// <summary>Construct an instance of this object, storing information on the specified method</summary>
/// <param name="assembly">The name/path of the assembly/DLL to load from</param>
/// <param name="className">The fully qualified, case-sensitive class name</param>
/// <param name="methodName">The case-sensitive name of the method to get</param>
public ExternalFuncObject( string assembly, string className, string methodName )
: this( assembly, className, methodName, false )
{
return;
}
/// <summary>Construct an instance of this object, storing information on the specified method</summary>
/// <param name="assembly">The name/path of the assembly/DLL to load from</param>
/// <param name="className">The fully qualified, case-sensitive class name</param>
/// <param name="methodName">The case-sensitive name of the method to get</param>
/// <param name="isSecret">Is this a secret/hidden ExFunc?</param>
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;
}
/// <summary>Attempt to invoke the previously loaded method dynamically</summary>
/// <param name="parameters">Parameters that should be passed to the method, or null if none</param>
/// <returns>A string return value from the method, or String.Empty if a null is returned or if the method returns void</returns>
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 );
}
/// <summary>Provide a string representation of this object</summary>
/// <returns>A string containing a representation of the object</returns>
public override string ToString()
{
return ( String.Format( "{0}::{1}", m_classInstance, MethodName ) );
}
#region IComparable<FuncObject> Members
/// <summary>Compare this object with another object instance</summary>
/// <param name="other">The object to compare to</param>
/// <returns>The result of comparing the object's MethodName strings</returns>
public int CompareTo( ExternalFuncObject other )
{
return ( MethodName.CompareTo( other.MethodName ) );
}
#endregion
}
#endregion
#region Binding Object
/// <summary>This object stores information on a bound key</summary>
protected class BindingObject : IComparable< BindingObject >
{
/// <summary>Modifier keys for this key-binding</summary>
[Flags]
public enum EModifier
{
/// <summary>No modifier keys associated with this Binding</summary>
None = 0,
/// <summary>CTRL modifier key is associated with this Binding</summary>
Ctrl = 1,
/// <summary>SHIFT modifier key is associated with this Binding</summary>
Shift = 2,
/// <summary>ALT modifier key is associated with this Binding</summary>
Alt = 4,
}
/// <summary>The command/function to execute when the bound key is hit</summary>
public string _text;
/// <summary>The key that the command/function is bound to</summary>
public Keys _key;
/// <summary>The modifier key for _key (Ctrl, Alt, Shift) - modifiers use the LEFT version of the key identifier</summary>
public EModifier _modifierKeys;
/// <summary>Gets the modifier key(s) in human readable form</summary>
/// <returns>A string representing this binding's modifiers, or an empty string if none</returns>
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() );
}
/// <summary>Convert this binding object into an informational string</summary>
/// <returns>The informational string</returns>
public override string ToString()
{
return ( String.Format( "<{0}{1}> -> {2}", GetModifierString(), _key, _text ) );
}
#region IComparable<BindingObject> Members
/// <summary>Compare this object with another object instance</summary>
/// <param name="other">The object to compare to</param>
/// <returns>Nothing - Not Implemented Yet</returns>
public int CompareTo( BindingObject other )
{
throw new NotImplementedException();
}
#endregion
}
#endregion
#region Fields
#region Command Processing
// -- Command Processing-Related Fields...
/// <summary>Collection Of Command Objects.</summary>
protected SortedDictionary<string,CmdObject> ms_commands = new SortedDictionary<string, CmdObject>();
/// <summary>Collection Of function Objects.</summary>
protected SortedDictionary<string, FuncObject> ms_functions = new SortedDictionary<string, FuncObject>();
/// <summary>Collection Of External Functions.</summary>
protected SortedDictionary<string, ExternalFuncObject> ms_externalFunctions = new SortedDictionary<string, ExternalFuncObject>();
/// <summary>Collection Of Binding Objects.</summary>
protected List<BindingObject> ms_bindings = new List<BindingObject>( 8 );
/// <summary>Colleciton Of Partial Command Matches</summary>
protected List<string> ms_partialCmdMatches = new List<string>( 8 );
/// <summary>Symbol table for the console variables</summary>
protected Dictionary<string, CVar> m_cVars = new Dictionary<string, CVar>( 8 );
/// <summary>Match index for the last partial command</summary>
protected int m_cmdMatchIndex = 0;
/// <summary>The last command match found</summary>
protected string m_lastCmdMatch = String.Empty;
/// <summary>The command line itself</summary>
protected string m_commandLine = String.Empty;
/// <summary>Function-related functions are locked out</summary>
protected bool m_functionsLocked = false;
/// <summary>Binding-related functions are locked out</summary>
protected bool m_bindingsLocked = false;
/// <summary>External-Function-related functions are locked out</summary>
protected bool m_exfunsLocked = false;
#endregion
#region Drawing and Graphics
// -- Drawing/Graphics-Related Fields...
/// <summary>Drawing object</summary>
protected SpriteBatch m_spriteBatch;
/// <summary>Font used for drawing the console's text</summary>
protected SpriteFont m_consoleFont;
/// <summary>Rectangle for the console area</summary>
protected Rectangle m_consoleRect;
/// <summary>Location of the command line</summary>
protected Vector2 m_commandPos;
/// <summary>Hight for the current console font</summary>
protected Vector2 m_stringHeight;
/// <summary>Offset for the command line</summary>
protected float m_commandStringHeightOffset;
/// <summary>Number of characters that can fit in the console area's width</summary>
protected int m_screenCharWidth;
/// <summary>Width of the console area</summary>
protected int m_width;
/// <summary>Height of the console area</summary>
protected int m_height;
/// <summary>Scale for drawing the console</summary>
protected float m_scale = 1.0f;
#endregion
#region Scrolling and Logging
// -- Scrolling-Related Fields...
/// <summary>The number of lines that are visible in the console area</summary>
protected int m_linesVisibleOnScreen;
/// <summary>Used for drawing an indicator to show that lines are scrolled above</summary>
protected bool m_linesAbove;
/// <summary>Used for drawing an indicator to show that lines are scrolled below</summary>
protected bool m_linesBelow;
/// <summary>Where the log starts at (for scrolling)</summary>
protected int m_logStart = 0;
/// <summary>Specifies the limit of the log buffer</summary>
protected int m_logLimit = 512;
// -- Logging-Related Fields...
/// <summary>The path to the log shadow (copy of all logged lines)</summary>
protected string m_logShadowFilePath = String.Empty;
/// <summary>Flag indicating that the log is being shadowed</summary>
protected bool m_logShadowEnabled = false;
/// <summary>The stream for shadowing the log to</summary>
protected StreamWriter m_logShadowFile = null;
/// <summary>Storage for the log</summary>
protected static List<string> m_log = new List<string>( 256 );
#endregion
#region Command History
// Command History-Related Fields
/// <summary>Specifies the limit of the command history</summary>
protected int m_cmdHistoryLimit = 512;
/// <summary>Collection of command history</summary>
protected static List<string> m_cmdHistory = new List<string>( 128 );
/// <summary>Current index in the command history</summary>
protected int m_cmdHistoryIndex = 0;
/// <summary>Scanning (up/down) index in of the command history</summary>
protected int m_cmdHistoryScanIndex = 0;
#endregion
#region Input Processing
// Input related fields
/// <summary>Time for key repeat</summary>
protected DateTime m_keyRepeatTime = DateTime.MinValue;
/// <summary>Character translation for ALL keyboard keys - contains an element for all values in the Keys enumeration</summary>
protected List<String> m_xlateAllKeys;
/// <summary>Character translation for ALL keyboard keys, shifted - contains an element for all values in the Keys enumeration</summary>
protected List<String> m_xlateAllKeysShifted;
#endregion
#endregion
#region Events
/// <summary>Event fired right before handling/execution of a command. Allows interception of normal command processing.</summary>
public event EventHandler<CommandConsoleEventArgs> PreCommandExecutedEvent;
/// <summary>Event fired when a console variable has been created/modified</summary>
public event EventHandler<CVarModifiedEventArgs> CVarModifiedEvent;
#endregion
#region Properties
/// <summary>Interface to the keyboard used for console input</summary>
public IConsoleKeyboard Keyboard
{
get;
protected set;
}
/// <summary>Image/Texture used to fade the text underneath the console's text area</summary>
public Texture2D FadeImage
{
get;
set;
}
/// <summary>The color used to produce the fade</summary>
public Color FadeColor
{
get;
set;
}
/// <summary>Drawing scale for the console</summary>
public float Scale
{
get
{
return m_scale;
}
set
{
m_scale = value;
m_stringHeight = Vector2.Zero;
}
}
/// <summary>Indicates if the console is active or not</summary>
public static bool Active
{
get;
set;
}
/// <summary>The prompt shown for the command line</summary>
public string Prompt
{
get;
set;
}
/// <summary>The color used to display entered commands</summary>
public Color CommandColor
{
get;
set;
}
/// <summary>The color used to display error messages</summary>
public Color ErrorColor
{
get;
set;
}
/// <summary>The color used to display output from executed commands</summary>
public Color OutputColor
{
get;
set;
}
/// <summary>The color used to display "normal" output messages</summary>
public Color NormalColor
{
get;
set;
}
/// <summary>The font used for text display</summary>
public SpriteFont Font
{
get { return( m_consoleFont ); }
set
{
m_consoleFont = value;
m_stringHeight = Vector2.Zero;
}
}
/// <summary>Sets/Gets the height of the console window</summary>
public int Height
{
get
{
return( m_height );
}
set
{
m_height = value;
m_stringHeight = Vector2.Zero;
}
}
/// <summary>Sets/Gets the width of the console window</summary>
public int Width
{
get
{
return( m_width );
}
set
{
m_width = value;
m_stringHeight = Vector2.Zero;
}
}
#endregion
#region Initialization
/// <summary>Constructor for this base class</summary>
/// <param name="game">The Game object for the owning/managing game</param>
public CommandConsoleBase( Game game )
: this( game, null )
{
return;
}
/// <summary>Constructor for this base class</summary>
/// <param name="game">The Game object for the owning/managing game</param>
/// <param name="fontToUse">The font that the console should use to draw its text</param>
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;
}
/// <summary>Primary initialization function</summary>
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<String>( 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<String>( 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
/// <summary>Load content for this component</summary>
/// <param name="content">The ContentManager that should be used </param>
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
/// <summary>
/// Recalculates measurements required for text display and wrapping. Sets the m_consoleRect,
/// m_commandPos and m_linesVisibleOnScreen fields.
/// </summary>
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;
}
/// <summary>Update this component</summary>
/// <param name="gameTime">Game's time</param>
public override void Update( GameTime gameTime )
{
CheckInput();
base.Update( gameTime );
}
/// <summary>Draw the console</summary>
/// <param name="gameTime">Game's time</param>
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
/// <summary>Process a keyboard key. Assumes that the Keyboard property is not null!</summary>
/// <param name="key">The key to process</param>
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;
}
/// <summary>Check input from the provided keyboard interface</summary>
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;
}
/// <summary>Get binding information from a "binding string" input by the user</summary>
/// <param name="bindString">The binding string to parse</param>
/// <param name="bindModifiers">ref - the modifier keys for the binding</param>
/// <param name="bindKey">ref - the key for the binding</param>
/// <returns>true if the binding string was understood, false if not</returns>
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 );
}
/// <summary>Check for bound keys - bound keys use the &lt;ALT&gt; modifier</summary>
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;
}
/// <summary>Build command matches for the specified (sub)string.</summary>
/// <param name="cmdMatch">The partial command to try to match</param>
/// <returns>Nothing</returns>
/// <remarks>Resets the ms_partialCmdMatches collection</remarks>
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
/// <summary>Add executed command output to the log.</summary>
/// <param name="text">The output text to add</param>
public void AddOutputToLog( string text )
{
if( String.IsNullOrEmpty( text ) )
{
return;
}
AddToLog( "\x03" + DateTime.Now.ToString( "hh:mm:ss.fff" ) + "-" + text );
return;
}
/// <summary>Add error output to the log.</summary>
/// <param name="text">The error text to add</param>
public void AddErrorToLog( string text )
{
if( String.IsNullOrEmpty( text ) )
{
return;
}
AddToLog( "\x02" + DateTime.Now.ToString( "hh:mm:ss.fff" ) + "-" + text );
return;
}
/// <summary>Add executed command to the log.</summary>
/// <param name="text">The command text to add</param>
public void AddCommandToLog( string text )
{
if( String.IsNullOrEmpty( text ) )
{
return;
}
AddToLog( "\x01" + DateTime.Now.ToString( "hh:mm:ss.fff" ) + "-" + text );
return;
}
/// <summary>Method to add generic output text to the log</summary>
/// <param name="text">The text to add</param>
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<string> 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();
/// <summary>
/// Break text up into separate lines to make it fit.
/// (Originally From MS Provided Code - RBG Starter Kit/Template - Text Helper Methods)
/// </summary>
/// <param name="text">The text to be broken up.</param>
/// <param name="font">The font used to measure the width of the text.</param>
/// <param name="rowWidth">The maximum width of each line, in pixels.</param>
/// <param name="scale">Scale that should be used for the lines.</param>
protected static List<string> BreakTextIntoList( string text, SpriteFont font,
int rowWidth, float scale )
{
// check parameters
if( String.IsNullOrEmpty( text ) )
{
return( new List<string>( new[] { String.Empty } ) );
}
if( font == null )
{
throw new ArgumentNullException( "font" );
}
if( rowWidth <= 0 )
{
throw new ArgumentOutOfRangeException( "rowWidth" );
}
// create the list
List<string> lines = new List<string>( 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
/// <summary>Sets up the standard/built-in commands.</summary>
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<string[]>( CommandConsoleBase_Help );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "quit";
cmdStruct.CommandHelp = "immediately quit the game";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_Quit );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "close";
cmdStruct.CommandHelp = "close the command console.";
cmdStruct.CmdEvent += new Action<string[]>( 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 <heightvalue>";
cmdStruct.CmdEvent +=new Action<string[]>(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<scale>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <name> [Type] <value> -- examples: cvar myVar MyValue, cvar myInt Int32 42";
cmdStruct.CmdEvent += new Action<string[]>( 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<string[]>( CommandConsoleBase_CVars );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "gc";
cmdStruct.CommandHelp = "immediately perform full garbage collection";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath> <true/false>";
cmdStruct.CmdEvent += new Action<string[]>( 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<string[]>( CommandConsoleBase_NoShadowLog );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "clear";
cmdStruct.CommandHelp = "clears the console log";
cmdStruct.CmdEvent += new Action<string[]>( 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 <numlines>";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_LogLimit );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "memoryinfo";
cmdStruct.CommandHelp = "display memory info";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_MemInfo );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "exfunc";
cmdStruct.CommandHelp = "load external function from an assembly";
cmdStruct.CommandHelpDetailed = "exfunc <assemblypath> <classname> <functionname>";
cmdStruct.MinParameters = 3;
cmdStruct.MaxParameters = 3;
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_ExFunc );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "systeminfo";
cmdStruct.CommandHelp = "display system info";
cmdStruct.CmdEvent += new Action<string[]>( 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 <durationms>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath>";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_SaveLog );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "functions";
cmdStruct.CommandHelp = "display all current functions";
cmdStruct.CmdEvent += new Action<string[]>( 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<string[]>( CommandConsoleBase_NoFunctions );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "exfuncs";
cmdStruct.CommandHelp = "display all current external functions";
cmdStruct.CmdEvent += new Action<string[]>( 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<string[]>( CommandConsoleBase_NoExFunctions );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "bindings";
cmdStruct.CommandHelp = "display all current key bindings";
cmdStruct.CmdEvent += new Action<string[]>( 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<string[]>( CommandConsoleBase_NoBindings );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "resetbindings";
cmdStruct.CommandHelp = "clear all key bindings";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_ResetBindings );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "resetexfuncs";
cmdStruct.CommandHelp = "clear all external functions";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_ResetExFuncs );
ms_commands[ cmdStruct.Command ] = cmdStruct;
cmdStruct = new CmdObject();
cmdStruct.Command = "resetfunctions";
cmdStruct.CommandHelp = "clear all functions";
cmdStruct.CmdEvent += new Action<string[]>( 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 <key> <commandtext/functionname> -- examples: 'bind ALT+G gc', 'bind CTRL+SHIFT+Q quit'";
cmdStruct.CmdEvent += new Action<string[]>( 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 <key>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <functionname> <line>;<line>;<...>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath>";
cmdStruct.CmdEvent += new Action<string[]>( 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 <filepath>";
cmdStruct.CmdEvent += new Action<string[]>( CommandConsoleBase_ExportState );
ms_commands[ cmdStruct.Command ] = cmdStruct;
return;
}
/// <summary>
/// Overridable method used to add custom commands to the console.
/// </summary>
public virtual void InitializeCustomCommands()
{
return;
}
/// <summary>Initialize the Console's Commands -- is normally called via <see cref="LoadContent"/></summary>
public void InitializeCommands()
{
InitializeStandardCommands();
InitializeCustomCommands();
return;
}
/// <summary>
/// Add a command to the internal collection of commands. Throws an exception if duplicate identifiers are detected.
/// </summary>
/// <param name="cmdStruct">Command object to add</param>
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;
}
/// <summary>
/// Add a function to the internal collection of functions. Throws an exception if duplicate identifiers are detected.
/// </summary>
/// <param name="fnStruct">Function object to add</param>
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
/// <summary>Execute the specified Function</summary>
/// <param name="fnStruct">Function to execute</param>
/// <param name="parameters">Parameters for the function</param>
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;
}
/// <summary>Evaluates and executes the command line stored in the "_command" field.</summary>
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;
}
/// <summary>Handle set/get cvar</summary>
/// <param name="cmds">Command Line</param>
/// <param name="cmd">Pre-processed command</param>
/// <returns>true if command was handled by this function</returns>
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 );
}
/// <summary>Execute an internal function</summary>
/// <param name="cmds">Command Line</param>
/// <param name="cmd">Pre-processed command</param>
/// <returns>true if command was handled by this function</returns>
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 );
}
/// <summary>Execute a previously loaded external function</summary>
/// <param name="cmds">Command Line</param>
/// <param name="cmd">Pre-processed command</param>
/// <returns>true if command was handled by this function</returns>
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<object> cVarParams = new List<object>( 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;
}
/// <summary>Execute a normal command</summary>
/// <param name="cs">Command Object</param>
/// <param name="cmds">Command Line</param>
/// <param name="cmd">Pre-processed command</param>
/// <returns>true if command was handled by this function</returns>
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
/// <summary>Try to extract a double/Double value from the string</summary>
/// <param name="value">Value to parse</param>
/// <param name="parsedValue">out - receives the value parsed from the string</param>
/// <param name="errorIfParsingFails">Error msg to log if parsing fails</param>
/// <returns>True if parsed successfully, false otherwise</returns>
protected bool TryParseDouble( string value, out double parsedValue, string errorIfParsingFails )
{
if( !Double.TryParse( value, out parsedValue ) )
{
AddErrorToLog( errorIfParsingFails );
return ( false );
}
return ( true );
}
/// <summary>Try to extract a float/Single value from the string</summary>
/// <param name="value">Value to parse</param>
/// <param name="parsedValue">out - receives the value parsed from the string</param>
/// <param name="errorIfParsingFails">Error msg to log if parsing fails</param>
/// <returns>True if parsed successfully, false otherwise</returns>
protected bool TryParseFloat( string value, out float parsedValue, string errorIfParsingFails )
{
if( !Single.TryParse( value, out parsedValue ) )
{
AddErrorToLog( errorIfParsingFails );
return ( false );
}
return ( true );
}
/// <summary>Try to extract an int/Int32 value from the string</summary>
/// <param name="value">Value to parse</param>
/// <param name="parsedValue">out - receives the value parsed from the string</param>
/// <param name="errorIfParsingFails">Error msg to log if parsing fails</param>
/// <returns>True if parsed successfully, false otherwise</returns>
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 <command>." );
}
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;
}
/// <summary>Type converter helper - tries to convert the specified string to the specified type</summary>
/// <param name="type">The target type to convert to</param>
/// <param name="mystring">The source string to try to convert</param>
/// <returns>A reference to the successfully converted object, or null if conversion failed</returns>
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<string> GetAllExFuncs()
{
List<string> exFuncs = new List<string>( ms_externalFunctions.Count );
foreach( ExternalFuncObject efo in ms_externalFunctions.Values )
{
exFuncs.Add( efo.CommandLine );
}
return( exFuncs );
}
private List<string> GetAllFunctions()
{
List<string> functions = new List<string>( 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<string> bindings = new List<string>( 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;
}
/// <summary>Special command handler for ExportState command</summary>
/// <param name="cmdLine">The command line for the command</param>
/// <remarks>This one is protected virtual so additional state can be stored if necessary by derived classes</remarks>
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<string> bindings = GetAllBindings();
foreach( string line in bindings )
{
outFile.WriteLine( line );
}
}
}
if( ms_functions.Count > 0 )
{
lock( ms_functions )
{
List<string> 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 <ft.Length; line++ )
{
ft[ line ] = ft[ line ].Trim().ToLowerInvariant();
if( ft[ line ].Length == 0 )
{
continue;
}
hasCode = true;
}
if( !hasCode )
{
AddErrorToLog( "Function contains no code to execute." );
}
else
{
FuncObject newFN = new FuncObject( cmdLine[ 1 ], ft );
newFN.IsSecret = isSecret;
AddFunction( newFN );
}
return;
}
#endregion
}
}
#endif