You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
663 lines
20 KiB
663 lines
20 KiB
// |
|
// Copyright (c) 2010-2011 Matthew Jack and Doug Binks |
|
// |
|
// This software is provided 'as-is', without any express or implied |
|
// warranty. In no event will the authors be held liable for any damages |
|
// arising from the use of this software. |
|
// Permission is granted to anyone to use this software for any purpose, |
|
// including commercial applications, and to alter it and redistribute it |
|
// freely, subject to the following restrictions: |
|
// 1. The origin of this software must not be misrepresented; you must not |
|
// claim that you wrote the original software. If you use this software |
|
// in a product, an acknowledgment in the product documentation would be |
|
// appreciated but is not required. |
|
// 2. Altered source versions must be plainly marked as such, and must not be |
|
// misrepresented as being the original software. |
|
// 3. This notice may not be removed or altered from any source distribution. |
|
|
|
#include "Console.h" |
|
|
|
#include "../../RuntimeCompiler/BuildTool.h" |
|
#include "../../RuntimeCompiler/ICompilerLogger.h" |
|
#include "../../RuntimeObjectSystem/ObjectInterface.h" |
|
#include "../../RuntimeObjectSystem/IObjectFactorySystem.h" |
|
#include "../../RuntimeObjectSystem/IRuntimeObjectSystem.h" |
|
#include "../../RuntimeObjectSystem/IObject.h" |
|
#include "../../Systems/ILogSystem.h" |
|
#include "../../Systems/IEntitySystem.h" |
|
#include "../../Systems/IGame.h" |
|
#include "../../Systems/SystemTable.h" |
|
#include "../../RuntimeObjectSystem/RuntimeProtector.h" |
|
#include "Environment.h" |
|
#include "IConsoleContext.h" |
|
#include "IObjectUtils.h" |
|
|
|
#include <assert.h> |
|
#include <fstream> |
|
#include <algorithm> |
|
#ifndef _WIN32 |
|
#include <string.h> |
|
int stricmp( const char* pS1, const char* pS2 ) |
|
{ |
|
return strcasecmp( pS1, pS2 ); |
|
} |
|
#endif |
|
|
|
#define CONSOLE_INPUT_FILE "Console.txt" |
|
#define CONSOLE_CONTEXT_FILE "ConsoleContext.cpp" |
|
|
|
// Remove windows.h define of GetObject which conflicts with EntitySystem GetObject |
|
#if defined _WINDOWS_ && defined GetObject |
|
#undef GetObject |
|
#endif |
|
|
|
using FileSystemUtils::Path; |
|
|
|
|
|
static const char* CONTEXT_HEADER = |
|
"// Generated/modified by Console.cpp during runtime - safe to delete \n" |
|
"#include \"../../RuntimeObjectSystem/ObjectInterfacePerModule.h\" \n" |
|
"#include \"../../Systems/SystemTable.h\" \n" |
|
"#include \"../../RuntimeObjectSystem/IObjectFactorySystem.h\" \n" |
|
"#include \"../../Systems/ILogSystem.h\" \n" |
|
"#include \"ConsoleContext.h\" \n\n" |
|
"REGISTERCLASS(ConsoleContext); \n\n" |
|
"void ConsoleContext::Execute(SystemTable* sys) \n" |
|
"{ \n"; |
|
|
|
static const char* CONTEXT_FOOTER = |
|
"\n}"; |
|
|
|
Console::Console(Environment* pEnv, Rocket::Core::Context* pRocketContext) |
|
: m_pEnv(pEnv) |
|
, m_pRocketContext(pRocketContext) |
|
, m_bWaitingForCompile(false) |
|
, m_bCurrentContextFromGUI(false) |
|
, m_bGUIVisible(false) |
|
, m_bGUIViewMulti(false) |
|
, m_pDocument(0) |
|
, m_pViewButton(0) |
|
, m_pBackButton(0) |
|
, m_pForwardButton(0) |
|
, m_pCloseButton(0) |
|
, m_pExecuteButton(0) |
|
, m_pSingleLineArea(0) |
|
, m_pMultiLineArea(0) |
|
{ |
|
AU_ASSERT(m_pEnv && m_pRocketContext); |
|
|
|
Path basepath = Path(__FILE__).ParentPath(); |
|
basepath = m_pEnv->sys->pRuntimeObjectSystem->FindFile( basepath ); |
|
m_inputFile = basepath / Path(CONSOLE_INPUT_FILE); |
|
m_contextFile = basepath / Path(CONSOLE_CONTEXT_FILE); |
|
|
|
m_contextFile.ToOSCanonicalCase(); |
|
|
|
if( CreateConsoleContextFile() ) |
|
{ |
|
InitFileChangeNotifier(); |
|
} |
|
|
|
m_textAreaParams[ETAT_SINGLE].history.push_back(""); |
|
m_textAreaParams[ETAT_SINGLE].position = 0; |
|
m_textAreaParams[ETAT_MULTI].history.push_back(""); |
|
m_textAreaParams[ETAT_MULTI].position = 0; |
|
|
|
InitGUI(); |
|
} |
|
|
|
Console::~Console() |
|
{ |
|
m_contextFile.Remove(); |
|
|
|
// Call just in case it wasn't already called |
|
DestroyContext(); |
|
} |
|
|
|
void Console::DestroyContext() |
|
{ |
|
if (m_contextId.IsValid()) |
|
{ |
|
delete m_pEnv->sys->pObjectFactorySystem->GetObject( m_contextId ); |
|
m_contextId = ObjectId(); // set to invalid value |
|
} |
|
} |
|
|
|
void Console::OnFileChange(const IAUDynArray<const char*>& filelist) |
|
{ |
|
if (!stricmp(filelist[0], m_inputFile.c_str())) |
|
{ |
|
// This is a console compilation notification |
|
|
|
if (!m_bWaitingForCompile) |
|
{ |
|
m_bWaitingForCompile = true; |
|
m_bCurrentContextFromGUI = false; |
|
WriteConsoleContext(); |
|
} |
|
else |
|
{ |
|
m_pEnv->sys->pLogSystem->Log(eLV_WARNINGS, "Received console code while still waiting for last compile to complete\n"); |
|
} |
|
} |
|
else |
|
{ |
|
// This is a GUI file change notification |
|
|
|
ReloadGUI(); |
|
} |
|
} |
|
|
|
void Console::OnCompileDone(bool bSuccess) |
|
{ |
|
if (m_bWaitingForCompile) |
|
{ |
|
m_bWaitingForCompile = false; |
|
|
|
// Remove temp context file from game runtime file list so it isn't included in full recompiles |
|
// This must be done every time ConsoleContext.cpp gets recompiled because it will get registered again |
|
m_pEnv->sys->pRuntimeObjectSystem->RemoveFromRuntimeFileList(m_contextFile.c_str()); |
|
|
|
if (bSuccess) |
|
{ |
|
// Create console context on first compile |
|
if (m_contextId.m_PerTypeId == InvalidId) |
|
{ |
|
CreateConsoleContext(); |
|
} |
|
|
|
ExecuteConsoleContext(); |
|
} |
|
|
|
if (m_bCurrentContextFromGUI) |
|
{ |
|
if (bSuccess) |
|
{ |
|
ApplyGUIClear(); |
|
} |
|
|
|
ApplyGUIFinishExecute(); |
|
} |
|
} |
|
} |
|
|
|
void Console::InitFileChangeNotifier() |
|
{ |
|
// Make filechangenotifier monitor console code file and notify here |
|
m_pEnv->sys->pFileChangeNotifier->Watch(m_inputFile.c_str(), this); |
|
|
|
// Make filechangenotifier watch temp context file for changes and notify regular listener for recompile |
|
m_pEnv->sys->pRuntimeObjectSystem->AddToRuntimeFileList( m_contextFile.c_str() ); |
|
//m_pEnv->sys->pFileChangeNotifier->Watch(m_contextFile.string().c_str(), m_pEnv->sys->pRuntimeObjectSystem); |
|
|
|
// Make filechangenotifier watch console RML/RCSS files |
|
Path basepath = Path(__FILE__).ParentPath(); |
|
std::string filename = (basepath / Path("/../../Assets/GUI/console.rml")).m_string; |
|
m_pEnv->sys->pFileChangeNotifier->Watch(filename.c_str(), this); |
|
filename = (basepath / Path("/../../Assets/GUI/console.rcss")).m_string; |
|
m_pEnv->sys->pFileChangeNotifier->Watch(filename.c_str(), this); |
|
} |
|
|
|
void Console::InitGUI() |
|
{ |
|
// Load document but don't show it yet |
|
Rocket::Core::ElementDocument* pDocument = m_pRocketContext->LoadDocument("/GUI/console.rml"); |
|
if (pDocument != NULL) |
|
{ |
|
pDocument->SetId( "Console" ); |
|
pDocument->RemoveReference(); |
|
|
|
InitGUIReferences(); |
|
InitGUIEvents(); |
|
ApplyCurrentGUIState(); |
|
} |
|
} |
|
|
|
void Console::ReloadGUI() |
|
{ |
|
// Clear style sheet cache so any changes to RCSS files will be applied |
|
Rocket::Core::Factory::ClearStyleSheetCache(); |
|
|
|
Rocket::Core::ElementDocument* pDocument = m_pRocketContext->LoadDocument("/GUI/console.rml"); |
|
if (pDocument != NULL) |
|
{ |
|
pDocument->SetId( "Console" ); |
|
pDocument->SetOffset(m_pDocument->GetRelativeOffset(), NULL); |
|
pDocument->RemoveReference(); |
|
m_pRocketContext->UnloadDocument(m_pDocument); |
|
|
|
InitGUIReferences(); |
|
InitGUIEvents(); |
|
ApplyCurrentGUIState(); |
|
} |
|
} |
|
|
|
void Console::InitGUIReferences() |
|
{ |
|
m_pDocument = m_pRocketContext->GetDocument("Console"); |
|
AU_ASSERT(m_pDocument); |
|
|
|
m_pViewButton = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("ViewButton")); |
|
m_pClearButton = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("ClearButton")); |
|
m_pBackButton = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("BackButton")); |
|
m_pForwardButton = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("ForwardButton")); |
|
m_pCloseButton = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("CloseButton")); |
|
m_pExecuteButton = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("ExecuteButton")); |
|
m_pSingleLineArea = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("SingleLineArea")); |
|
m_pMultiLineArea = static_cast<Rocket::Controls::ElementFormControl*>(m_pDocument->GetElementById("MultiLineArea")); |
|
AU_ASSERT(m_pViewButton && m_pBackButton && m_pForwardButton && m_pCloseButton && m_pExecuteButton && m_pSingleLineArea && m_pMultiLineArea); |
|
|
|
m_textAreaParams[0].pElement = m_pSingleLineArea; |
|
m_textAreaParams[1].pElement = m_pMultiLineArea; |
|
} |
|
|
|
void Console::InitGUIEvents() |
|
{ |
|
m_pViewButton->AddEventListener("click", this); |
|
m_pClearButton->AddEventListener("click", this); |
|
m_pBackButton->AddEventListener("click", this); |
|
m_pForwardButton->AddEventListener("click", this); |
|
m_pCloseButton->AddEventListener("click", this); |
|
m_pExecuteButton->AddEventListener("click", this); |
|
m_pSingleLineArea->AddEventListener("textinput", this); |
|
} |
|
|
|
void Console::ProcessEvent(Rocket::Core::Event& event) |
|
{ |
|
Rocket::Core::Element* pElement = event.GetTargetElement(); |
|
std::string target = pElement->GetId().CString(); |
|
|
|
if (!stricmp(target.c_str(), "SingleLineArea")) |
|
{ |
|
// Character added to single line area - execute contents if the enter key was pressed |
|
Rocket::Core::word character = event.GetParameter< Rocket::Core::word >("data", 0); |
|
if (character == '\n') |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
params.position = (int)params.history.size() - 1; |
|
|
|
StoreGUITextInHistory(); |
|
ApplyGUIExecute(); |
|
} |
|
} |
|
else if (!stricmp(target.c_str(), "ExecuteButton")) |
|
{ |
|
// Execute contents of multi line area |
|
if (!m_pExecuteButton->IsDisabled()) |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
params.position = (int)params.history.size() - 1; |
|
|
|
StoreGUITextInHistory(); |
|
ApplyGUIExecute(); |
|
} |
|
else |
|
{ |
|
m_pExecuteButton->Blur(); |
|
} |
|
} |
|
else if (!stricmp(target.c_str(), "ViewButton")) |
|
{ |
|
// Toggle between single and multi line views |
|
m_bGUIViewMulti = !m_bGUIViewMulti; |
|
|
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
if (params.position == (int)params.history.size() - 1) |
|
{ |
|
StoreGUITextInHistory(); // save text in latest history before changing views so we don't lose it |
|
} |
|
|
|
ApplyGUIViewType(); |
|
ApplyGUIHistoryPosition(); // do this to set history buttons appropriately |
|
FocusOnTextArea(); |
|
} |
|
else if (!stricmp(target.c_str(), "ClearButton")) |
|
{ |
|
// Clear current text area |
|
ApplyGUIClear(); |
|
} |
|
else if (!stricmp(target.c_str(), "CloseButton")) |
|
{ |
|
// Close console |
|
m_bGUIVisible = false; |
|
ApplyGUIView(); |
|
} |
|
else if (!stricmp(target.c_str(), "BackButton")) |
|
{ |
|
// Go one step back in command history |
|
if (!m_pBackButton->IsDisabled()) |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
if (params.position == params.history.size() - 1) |
|
{ |
|
StoreGUITextInHistory(); // save text in latest history before starting to go back so we don't lose it |
|
} |
|
|
|
params.position--; |
|
params.position = std::max(0, params.position); |
|
|
|
ApplyGUIHistoryPosition(); |
|
FocusOnTextArea(); |
|
} |
|
else |
|
{ |
|
m_pBackButton->Blur(); |
|
} |
|
} |
|
else if (!stricmp(target.c_str(), "ForwardButton")) |
|
{ |
|
// Go one step forward in command history |
|
if (!m_pForwardButton->IsDisabled()) |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
params.position++; |
|
params.position = std::min((int)params.history.size(), params.position); |
|
|
|
ApplyGUIHistoryPosition(); |
|
FocusOnTextArea(); |
|
} |
|
else |
|
{ |
|
m_pForwardButton->Blur(); |
|
} |
|
} |
|
} |
|
|
|
void Console::StoreGUITextInHistory() |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
params.history[params.position] = params.pElement->GetValue().CString(); |
|
} |
|
|
|
void Console::ToggleGUI() |
|
{ |
|
m_bGUIVisible = !m_bGUIVisible; |
|
ApplyGUIView(); |
|
FocusOnTextArea(); |
|
} |
|
|
|
void Console::FocusOnTextArea() |
|
{ |
|
if (m_bGUIViewMulti) |
|
{ |
|
m_pMultiLineArea->Focus(); |
|
} |
|
else |
|
{ |
|
m_pSingleLineArea->Focus(); |
|
} |
|
} |
|
|
|
void Console::ApplyCurrentGUIState() |
|
{ |
|
ApplyGUIView(); |
|
ApplyGUIViewType(); |
|
ApplyGUIHistoryPosition(); |
|
FocusOnTextArea(); |
|
} |
|
|
|
void Console::ApplyGUIView() |
|
{ |
|
if (m_bGUIVisible) |
|
{ |
|
m_pDocument->Show(); |
|
} |
|
else |
|
{ |
|
m_pDocument->Hide(); |
|
} |
|
} |
|
|
|
void Console::ApplyGUIViewType() |
|
{ |
|
if (m_bGUIViewMulti) |
|
{ |
|
m_pMultiLineArea->SetProperty("display", "block"); |
|
m_pSingleLineArea->SetProperty("display", "none"); |
|
m_pExecuteButton->SetProperty("display", "block"); |
|
m_pViewButton->SetInnerRML("Single"); |
|
|
|
m_pMultiLineArea->SetProperty("tab-index", "auto"); |
|
m_pSingleLineArea->SetProperty("tab-index", "none"); |
|
m_pExecuteButton->SetProperty("tab-index", "auto"); |
|
} |
|
else |
|
{ |
|
m_pMultiLineArea->SetProperty("display", "none"); |
|
m_pSingleLineArea->SetProperty("display", "block"); |
|
m_pExecuteButton->SetProperty("display", "none"); |
|
m_pViewButton->SetInnerRML("Multi"); |
|
|
|
m_pMultiLineArea->SetProperty("tab-index", "none"); |
|
m_pSingleLineArea->SetProperty("tab-index", "auto"); |
|
m_pExecuteButton->SetProperty("tab-index", "none"); |
|
} |
|
} |
|
|
|
void Console::ApplyGUIClear() |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
params.pElement->SetValue(""); |
|
} |
|
|
|
void Console::ApplyGUIHistoryPosition() |
|
{ |
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
|
|
params.pElement->SetValue(params.history[params.position].c_str()); |
|
|
|
if (params.position == 0) |
|
{ |
|
m_pBackButton->SetDisabled(true); |
|
m_pBackButton->SetProperty("tab-index", "none"); |
|
m_pBackButton->SetPseudoClass("disabled", true); |
|
} |
|
else |
|
{ |
|
m_pBackButton->SetDisabled(false); |
|
m_pBackButton->SetProperty("tab-index", "auto"); |
|
m_pBackButton->SetPseudoClass("disabled", false); |
|
} |
|
|
|
if (params.position == params.history.size() - 1) |
|
{ |
|
m_pForwardButton->SetDisabled(true); |
|
m_pForwardButton->SetProperty("tab-index", "none"); |
|
m_pForwardButton->SetPseudoClass("disabled", true); |
|
} |
|
else |
|
{ |
|
m_pForwardButton->SetDisabled(false); |
|
m_pForwardButton->SetProperty("tab-index", "auto"); |
|
m_pForwardButton->SetPseudoClass("disabled", false); |
|
} |
|
} |
|
|
|
void Console::ApplyGUIExecute() |
|
{ |
|
if (!m_bWaitingForCompile) |
|
{ |
|
Rocket::Core::String rcText = m_bGUIViewMulti ? m_pMultiLineArea->GetValue() : m_pSingleLineArea->GetValue(); |
|
const std::string& text = rcText.Append(";").CString(); // Add ; to end to handle common error |
|
|
|
m_bWaitingForCompile = true; |
|
m_bCurrentContextFromGUI = true; |
|
WriteConsoleContext(text); |
|
|
|
// Disable Execute button and text area |
|
m_pExecuteButton->SetDisabled(true); |
|
m_pMultiLineArea->SetDisabled(true); |
|
m_pSingleLineArea->SetDisabled(true); |
|
m_pMultiLineArea->SetProperty("tab-index", "none"); |
|
m_pSingleLineArea->SetProperty("tab-index", "none"); |
|
m_pExecuteButton->SetProperty("tab-index", "none"); |
|
m_pExecuteButton->SetPseudoClass("disabled", true); |
|
} |
|
else |
|
{ |
|
m_pEnv->sys->pLogSystem->Log(eLV_WARNINGS, "Received console code from GUI while still waiting for last compile to complete\n"); |
|
} |
|
} |
|
|
|
void Console::ApplyGUIFinishExecute() |
|
{ |
|
// Enable Execute button and text area |
|
m_pExecuteButton->SetDisabled(false); |
|
m_pMultiLineArea->SetDisabled(false); |
|
m_pSingleLineArea->SetDisabled(false); |
|
m_pMultiLineArea->SetProperty("tab-index", "auto"); |
|
m_pSingleLineArea->SetProperty("tab-index", "auto"); |
|
m_pExecuteButton->SetProperty("tab-index", "auto"); |
|
m_pExecuteButton->SetPseudoClass("disabled", false); |
|
|
|
STextAreaParams& params = m_textAreaParams[m_bGUIViewMulti ? ETAT_MULTI : ETAT_SINGLE]; |
|
params.pElement->Focus(); |
|
params.history.push_back(""); // add new history element which corresponds to current input |
|
params.position = (int)params.history.size() - 1; |
|
|
|
ApplyGUIHistoryPosition(); |
|
} |
|
|
|
bool Console::CreateConsoleContextFile() |
|
{ |
|
bool bRet = true; |
|
std::ofstream outFile; |
|
outFile.open(m_contextFile.c_str(), std::ios::out | std::ios::trunc); |
|
if (!outFile) |
|
{ |
|
bRet = false; |
|
m_pEnv->sys->pLogSystem->Log(eLV_ERRORS, "Unable to create context file: %s\n", m_contextFile.c_str()); |
|
} |
|
|
|
return bRet; |
|
} |
|
|
|
// Only needs to be called once, on first compile of console context (not at program start) |
|
void Console::CreateConsoleContext() |
|
{ |
|
IObject *pObj = IObjectUtils::CreateObject( "ConsoleContext" ); |
|
pObj->GetObjectId( m_contextId ); |
|
} |
|
|
|
void Console::WriteConsoleContext() |
|
{ |
|
std::ifstream inFile; |
|
inFile.open(m_inputFile.c_str(), std::ios::in); |
|
if (!inFile) |
|
{ |
|
m_pEnv->sys->pLogSystem->Log(eLV_ERRORS, "Unable to open console input file for reading: %s\n", m_inputFile.c_str()); |
|
return; |
|
} |
|
|
|
std::ofstream outFile; |
|
outFile.open(m_contextFile.c_str(), std::ios::out | std::ios::trunc); |
|
if (!outFile) |
|
{ |
|
m_pEnv->sys->pLogSystem->Log(eLV_ERRORS, "Unable to open context file for writing: %s\n", m_contextFile.c_str()); |
|
return; |
|
} |
|
|
|
// Write header boilerplate |
|
outFile << CONTEXT_HEADER; |
|
|
|
// Write console code |
|
outFile << inFile.rdbuf(); |
|
|
|
// Write footer boilerplate |
|
outFile << CONTEXT_FOOTER; |
|
} |
|
|
|
void Console::WriteConsoleContext(const std::string& text) |
|
{ |
|
std::ofstream outFile; |
|
outFile.open(m_contextFile.c_str(), std::ios::out | std::ios::trunc); |
|
if (!outFile) |
|
{ |
|
m_pEnv->sys->pLogSystem->Log(eLV_ERRORS, "Unable to open context file for writing: %s\n", m_contextFile.c_str()); |
|
return; |
|
} |
|
|
|
// Write header boilerplate |
|
outFile << CONTEXT_HEADER; |
|
|
|
// Write console code |
|
outFile << text; |
|
|
|
// Write footer boilerplate |
|
outFile << CONTEXT_FOOTER; |
|
} |
|
|
|
// local class for console execution |
|
class ConsoleExecuteProtector : public RuntimeProtector |
|
{ |
|
public: |
|
IConsoleContext* pContext; |
|
SystemTable* pSys; |
|
private: |
|
virtual void ProtectedFunc() |
|
{ |
|
pContext->Execute( pSys ); |
|
} |
|
|
|
}; |
|
void Console::ExecuteConsoleContext() |
|
{ |
|
ILogSystem *pLog = m_pEnv->sys->pLogSystem; |
|
|
|
IConsoleContext* pContext; |
|
IObjectUtils::GetObject( &pContext, m_contextId ); |
|
AU_ASSERT(pContext); |
|
|
|
if (pContext) |
|
{ |
|
pLog->Log(eLV_COMMENTS, "Executing console context...\n"); |
|
|
|
// Console should execute Safe-C, but for some things we really do want to return |
|
// null pointers on occaision. So lets deal with those simple cases cleanly. |
|
ConsoleExecuteProtector consoleProtectedExecutor; |
|
consoleProtectedExecutor.m_bHintAllowDebug = false; |
|
consoleProtectedExecutor.pContext = pContext; |
|
consoleProtectedExecutor.pSys = m_pEnv->sys; |
|
m_pEnv->sys->pRuntimeObjectSystem->TryProtectedFunction( &consoleProtectedExecutor ); |
|
|
|
if( consoleProtectedExecutor.HasHadException() ) |
|
{ |
|
switch (consoleProtectedExecutor.ExceptionInfo.Type) |
|
{ |
|
case RuntimeProtector::ESE_Unknown: |
|
pLog->Log(eLV_ERRORS, "Console command caused an unknown error\n"); |
|
break; |
|
case RuntimeProtector::ESE_AccessViolation: |
|
// Note that in practice, writing to pointers it not something that should often be needed in a console command |
|
if (consoleProtectedExecutor.ExceptionInfo.Addr == 0) |
|
pLog->Log(eLV_ERRORS, "Console command tried to access a null pointer\n"); |
|
else |
|
pLog->Log(eLV_ERRORS, "Console command tried to access an invalid pointer (address 0x%p)\n", consoleProtectedExecutor.ExceptionInfo.Addr); |
|
break; |
|
case RuntimeProtector::ESE_AccessViolationRead: |
|
if (consoleProtectedExecutor.ExceptionInfo.Addr == 0) |
|
pLog->Log(eLV_ERRORS, "Console command tried to read from a null pointer\n"); |
|
else |
|
pLog->Log(eLV_ERRORS, "Console command tried to read from an invalid pointer (address 0x%p)\n", consoleProtectedExecutor.ExceptionInfo.Addr); |
|
break; |
|
case RuntimeProtector::ESE_AccessViolationWrite: |
|
// Note that in practice, writing to pointers it not something that should often be needed in a console command |
|
if (consoleProtectedExecutor.ExceptionInfo.Addr == 0) |
|
pLog->Log(eLV_ERRORS, "Console command tried to write to a null pointer\n"); |
|
else |
|
pLog->Log(eLV_ERRORS, "Console command tried to write to an invalid pointer (address 0x%p)\n", consoleProtectedExecutor.ExceptionInfo.Addr); |
|
break; |
|
case RuntimeProtector::ESE_InvalidInstruction: |
|
pLog->Log(eLV_ERRORS, "Console command tried to execute an invalid instruction at (address 0x%p)\n", consoleProtectedExecutor.ExceptionInfo.Addr); |
|
break; |
|
default: |
|
AU_ASSERT(false); |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
pLog->Log(eLV_ERRORS, "Unable to execute console context - no context found\n"); |
|
} |
|
}
|
|
|