@@ -45,13 +45,22 @@ const std::wstring SAY_CRASH_FIX_ID = L"unrealsdk_bl2_say_crash_fix";
4545// We could combine this with the say bypass, but by keeping them separate it'll let users disable
4646// one if they really want to
4747const std::wstring CONSOLE_COMMAND_FUNC = L" Engine.Console:ConsoleCommand" ;
48+ // This is the actual end point of all console commands, the above function normally calls through
49+ // into this one - but we needed to hook it to be able to manage the console history. If something
50+ // / directly calls `PC.ConsoleCommand("my_cmd")`, we need this hook to be able to catch it.
51+ const std::wstring PC_CONSOLE_COMMAND_FUNC = L" Engine.PlayerController:ConsoleCommand" ;
52+
4853const constexpr auto CONSOLE_COMMAND_TYPE = hook_manager::Type::PRE;
4954const std::wstring CONSOLE_COMMAND_ID = L" unrealsdk_bl2_console_command" ;
5055
5156const std::wstring INJECT_CONSOLE_FUNC = L" WillowGame.WillowGameViewportClient:PostRender" ;
5257const constexpr auto INJECT_CONSOLE_TYPE = hook_manager::Type::PRE;
5358const std::wstring INJECT_CONSOLE_ID = L" unrealsdk_bl2_inject_console" ;
5459
60+ // Would prefer to call a native function where possible, however best I can tell, OutputText is
61+ // actually implemented directly in unrealscript (along most of the console mechanics).
62+ BoundFunction console_output_text{};
63+
5564bool say_bypass_hook (hook_manager::Details& hook) {
5665 /*
5766 This is a native function so we don't have exact source, but we expect it's essentially:
@@ -69,6 +78,7 @@ bool say_bypass_hook(hook_manager::Details& hook) {
6978 static const auto command_property =
7079 hook.args ->type ->find_prop_and_validate <UStrProperty>(L" Command" _fn);
7180
81+ // Since these are different functions, we can't just forward the args struct, have to copy it
7282 hook.obj ->get <UFunction, BoundFunction>(console_command_func)
7383 .call <void , UStrProperty>(hook.args ->get <UStrProperty>(command_property));
7484 return true ;
@@ -201,7 +211,25 @@ bool console_command_hook(hook_manager::Details& hook) {
201211 hook.obj ->get <UFunction, BoundFunction>(save_config_func).call <void >();
202212 }
203213
204- LOG (INFO, L" >>> {} <<<" , line);
214+ /*
215+ This is a little awkward.
216+ Since we can't let execution though to the unreal function, we're responsible for printing the
217+ executed command line.
218+
219+ We do this via output text directly, rather than the LOG macro, so that it's not affected by the
220+ console log level, and so that it happens immediately (the LOG macro is queued, and can get out
221+ of order with respect to native engine messages).
222+
223+ However, for custom console commands it's also nice to see what the command was in the log file,
224+ since you'll see all their output too.
225+
226+ We don't really expose a "write to log file only", since it's not usually something useful, so
227+ as a compromise just use the LOG macro on the lowest possible log level, and assume the lowest
228+ people practically set their console log level to is dev warning.
229+ */
230+ auto msg = unrealsdk::fmt::format (L" >>> {} <<<" , line);
231+ console_output_text.call <void , UStrProperty>(msg);
232+ LOG (MIN, L" {}" , msg);
205233
206234 try {
207235 callback->operator ()(line.c_str (), line.size (), cmd_len);
@@ -212,9 +240,26 @@ bool console_command_hook(hook_manager::Details& hook) {
212240 return true ;
213241}
214242
215- // Would prefer to call a native function where possible, however best I can tell, OutputText is
216- // actually implemented directly in unrealscript (along most of the console mechanics).
217- BoundFunction console_output_text{};
243+ bool pc_console_command_hook (hook_manager::Details& hook) {
244+ static const auto command_property =
245+ hook.args ->type ->find_prop_and_validate <UStrProperty>(L" Command" _fn);
246+
247+ auto line = hook.args ->get <UStrProperty>(command_property);
248+
249+ auto [callback, cmd_len] = commands::impl::find_matching_command (line);
250+ if (callback == nullptr ) {
251+ return false ;
252+ }
253+
254+ // This hook does not go to console, so there's no extra processing to be done, we can just run
255+ // the callback immediately
256+ try {
257+ callback->operator ()(line.c_str (), line.size (), cmd_len);
258+ } catch (const std::exception& ex) {
259+ LOG (ERROR, " An exception occurred while running a console command: {}" , ex.what ());
260+ }
261+ return true ;
262+ }
218263
219264bool inject_console_hook (hook_manager::Details& hook) {
220265 hook_manager::remove_hook (INJECT_CONSOLE_FUNC, INJECT_CONSOLE_TYPE, INJECT_CONSOLE_ID);
@@ -247,6 +292,9 @@ void BL2Hook::inject_console(void) {
247292
248293 hook_manager::add_hook (CONSOLE_COMMAND_FUNC, CONSOLE_COMMAND_TYPE, CONSOLE_COMMAND_ID,
249294 &console_command_hook);
295+ hook_manager::add_hook (PC_CONSOLE_COMMAND_FUNC, CONSOLE_COMMAND_TYPE, CONSOLE_COMMAND_ID,
296+ &pc_console_command_hook);
297+
250298 hook_manager::add_hook (INJECT_CONSOLE_FUNC, INJECT_CONSOLE_TYPE, INJECT_CONSOLE_ID,
251299 &inject_console_hook);
252300}
0 commit comments