Unreal的quit控制台命令如何注册到dumpconsolecommands

一、问题

在UE进程运行的时候,可以在控制台通过DumpConsoleCommands来列出UE中所有可执行的命令。从实现上看,UE中所有的控制台命令最终都会通过Engine\Source\Runtime\Core\Private\HAL\ConsoleManager.cpp: FConsoleManager::AddConsoleObject进行注册;而quit命令在UE中并没有通过这个接口注册(可以通过断点确认),但是在执行DumpConsoleCommands时,这个命令却赫然出现在输出列表中。UE是如何实现的呢?

二、DumpConsoleCommands本身的注册

这个命令本身的注册是中规中矩的,就是通过RegisterConsoleCommand==>>AddConsoleObject进行注册
ConsoleManager.cpp
IConsoleManager::Get().RegisterConsoleCommand( TEXT( "DumpConsoleCommands" ), TEXT( "Dumps all console vaiables and commands and all exec that can be discovered to the log/console" ), ECVF_Default );

三、如何捕捉值在代码中实现的控制台命令

1、ConsoleCommandLibrary类的注释

这里的注释说明了实现方法,就是在执行命令时,首先执行一个特殊命令,并注册钩子,在具体命令调用FParse::Command的时候触发回调,将传入命令名字添加到列表中。
Engine\Source\Runtime\Core\Private\Misc\Parse.cpp
/**
* Needed for the console command "DumpConsoleCommands"
* How it works:
* - GConsoleCommandLibrary is set to point at a local instance of ConsoleCommandLibrary
* - a dummy command search is triggered which gathers all commands in a hashed set
* - sort all gathered commands in human friendly way
* - log all commands
* - GConsoleCommandLibrary is set 0
*/
class ConsoleCommandLibrary
{
public:
ConsoleCommandLibrary(const FString& InPattern);

~ConsoleCommandLibrary();

void OnParseCommand(const TCHAR* Match)
{
// -1 to not take the "*" after the pattern into account
if(FCString::Strnicmp(Match, *Pattern, Pattern.Len() - 1) == 0)
{
KnownNames.Add(Match);
}
}

const FString& Pattern;
TSet<FString> KnownNames;
};

2、DumpConsoleCommands命令触发的主体函数

这个函数通过ConsoleCommandLibrary局部变量注册GConsoleCommandLibrary钩子函数,然后调用SubSystem.Exec,从而触发各个在代码中解析的FParse::Command调用。
Engine\Source\Runtime\Core\Private\Misc\Parse.cpp
void ConsoleCommandLibrary_DumpLibrary(UWorld* InWorld, FExec& SubSystem, const FString& Pattern, FOutputDevice& Ar)
{
ConsoleCommandLibrary LocalConsoleCommandLibrary(Pattern);

FOutputDeviceNull Null;

bool bExecuted = SubSystem.Exec( InWorld, *Pattern, Null);
……
}

3、FParse::Command函数中的回调

在每次执行FParse::Command函数时,判断如果有GConsoleCommandLibrary指针注册,则调用OnParseCommand回调,并且通常是马上执行 return false,从而避免触发真正的命令执行过程。
bool FParse::Command( const TCHAR** Stream, const TCHAR* Match, bool bParseMightTriggerExecution )
{
#if !UE_BUILD_SHIPPING
if(GConsoleCommandLibrary)
{
GConsoleCommandLibrary->OnParseCommand(Match);

if(bParseMightTriggerExecution)
{
// Better we fail the test - we only wanted to find all commands.
return false;
}
}
……
}

四、副作用

这种方法虽然巧妙,但是也有副作用:它们虽然可以通过DumpConsoleCommands提示出来,但是不能智能提示。
比方说,当你输入时r.D,控制台会贴心的提示所有可能的命令,其中就包括r.DumpingMovie,因为这个命令通过IConsoleManager::Get().RegisterConsoleVariableRef(TEXT("r.DumpingMovie")进行了主动注册;
但是通过这种奇技淫巧注册的命令就不行智能提示,例如输入start的的时候就不会智能提示StartRemoteSession,因为这个命令没有主动注册,而只是把解析写在了代码中。

 

posted on 2021-07-02 20:32  tsecer  阅读(230)  评论(0编辑  收藏  举报

导航