|
59 | 59 |
|
60 | 60 | #include <polyhook2/PE/IatHook.hpp> |
61 | 61 |
|
| 62 | +#include <FilesystemWatcher.hpp> |
| 63 | + |
62 | 64 | namespace RC |
63 | 65 | { |
64 | 66 | // Commented out because this system (turn off hotkeys when in-game console is open) it doesn't work properly. |
@@ -1068,6 +1070,68 @@ namespace RC |
1068 | 1070 |
|
1069 | 1071 | on_program_start(); |
1070 | 1072 |
|
| 1073 | + FilesystemWatcher filesystem_watcher{}; |
| 1074 | + if (settings_manager.General.EnableAutoReloadingLuaMods) |
| 1075 | + { |
| 1076 | + filesystem_watcher.m_min_duration_between_notifications = std::chrono::milliseconds{100}; |
| 1077 | + for (const auto& mod : m_mods) |
| 1078 | + { |
| 1079 | + if (dynamic_cast<CppMod*>(mod.get())) |
| 1080 | + { |
| 1081 | + filesystem_watcher.add_dir(std::filesystem::path{get_mods_directory()} / mod->get_name() / "dlls"); |
| 1082 | + } |
| 1083 | + else if (dynamic_cast<LuaMod*>(mod.get())) |
| 1084 | + { |
| 1085 | + filesystem_watcher.add_dir(std::filesystem::path{get_mods_directory()} / mod->get_name() / "Scripts"); |
| 1086 | + } |
| 1087 | + } |
| 1088 | + filesystem_watcher.start_async_polling([&](const std::filesystem::path& file, bool match_all) { |
| 1089 | + ScopedThreadSynchronizer thread_synchronizer{filesystem_watcher.get_thread_state()}; |
| 1090 | + const auto mod_name = file.parent_path().filename(); |
| 1091 | + auto dir_name = file.filename().string(); |
| 1092 | + const auto is_cpp_mod = String::iequal(dir_name, "dlls"); |
| 1093 | + if (is_cpp_mod) |
| 1094 | + { |
| 1095 | + auto staged_file = file / mod_name; |
| 1096 | + staged_file.replace_extension(".dll"); |
| 1097 | + if (!std::filesystem::exists(staged_file)) |
| 1098 | + { |
| 1099 | + return; |
| 1100 | + } |
| 1101 | + // TODO: Unload the dll (uninstall). |
| 1102 | + // Delete 'main.dll'. |
| 1103 | + // Rename 'staged_file' to 'main.dll'. |
| 1104 | + // Load 'main.dll' (install & start). |
| 1105 | + |
| 1106 | + // TODO: To reload C++ mods, we need to add a way to unregister hooks, and then C++ mods need to unregister on they get notified that they're getting unloaded. |
| 1107 | + // For Lua mods, there's no notification, but we track all the hooks internally, so we can unregister automatically, we just need to |
| 1108 | + // call Lua::Uninstall, and We must also remove the keybinds like we do in UE4SSProgram::reinstall_mods. |
| 1109 | + // Unsure about loading and starting mods again. |
| 1110 | + } |
| 1111 | + else |
| 1112 | + { |
| 1113 | + auto mod = find_lua_mod_by_name(ensure_str(mod_name), IsInstalled::Yes, IsStarted::Yes); |
| 1114 | + if (!mod) |
| 1115 | + { |
| 1116 | + return; |
| 1117 | + } |
| 1118 | + m_pause_events_processing = true; |
| 1119 | + mod->uninstall(); |
| 1120 | + auto& mod_ref = *std::ranges::find_if(m_mods, [&](const std::unique_ptr<Mod>& mod_ptr) { |
| 1121 | + return mod_ptr.get() == mod; |
| 1122 | + }); |
| 1123 | + if (!mod_ref) |
| 1124 | + { |
| 1125 | + return; |
| 1126 | + } |
| 1127 | + mod_ref = std::make_unique<LuaMod>(*this, StringType{mod_ref->get_name()}, mod_ref->get_path()); |
| 1128 | + m_pause_events_processing = false; |
| 1129 | + Output::send(STR("Auto-reloading Lua mod '{}'\n"), mod_ref->get_name()); |
| 1130 | + mod_ref->start_mod(); |
| 1131 | + } |
| 1132 | + }); |
| 1133 | + } |
| 1134 | + |
1071 | 1135 | Output::send(STR("Event loop start\n")); |
1072 | 1136 | for (m_processing_events = true; m_processing_events;) |
1073 | 1137 | { |
@@ -1133,6 +1197,12 @@ namespace RC |
1133 | 1197 | } |
1134 | 1198 | } |
1135 | 1199 |
|
| 1200 | + if (settings_manager.General.EnableAutoReloadingLuaMods) |
| 1201 | + { |
| 1202 | + // Process any mod file changes, and wait until done. |
| 1203 | + process_sync_request(filesystem_watcher.get_thread_state()); |
| 1204 | + } |
| 1205 | + |
1136 | 1206 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); |
1137 | 1207 | ProfilerFrameMark(); |
1138 | 1208 | } |
@@ -1308,6 +1378,23 @@ namespace RC |
1308 | 1378 | } |
1309 | 1379 | } |
1310 | 1380 |
|
| 1381 | + auto UE4SSProgram::unregister_keydown_events_for_lua_mod(LuaMod* mod, AllMods all_mods) -> void |
| 1382 | + { |
| 1383 | +#ifdef HAS_INPUT |
| 1384 | + m_input_handler.get_events_safe([&](auto& key_set) { |
| 1385 | + std::erase_if(key_set.key_data, [&](auto& item) -> bool { |
| 1386 | + auto& [_, key_data] = item; |
| 1387 | + std::erase_if(key_data, [&](Input::KeyData& key_data) -> bool { |
| 1388 | + // custom_data == 1: Bind came from Lua, and custom_data2 is a pointer to LuaMod. |
| 1389 | + // custom_data == 2: Bind came from C++, and custom_data2 is a pointer to KeyDownEventData. Must free it. |
| 1390 | + return key_data.custom_data == 1 && (all_mods == AllMods::Yes || static_cast<LuaMod*>(key_data.custom_data2) == mod); |
| 1391 | + }); |
| 1392 | + return key_data.empty(); |
| 1393 | + }); |
| 1394 | + }); |
| 1395 | +#endif |
| 1396 | + } |
| 1397 | + |
1311 | 1398 | template <typename ModType> |
1312 | 1399 | auto start_mods() -> std::string |
1313 | 1400 | { |
@@ -1506,31 +1593,6 @@ namespace RC |
1506 | 1593 |
|
1507 | 1594 | uninstall_mods(); |
1508 | 1595 |
|
1509 | | -// Remove key binds that were set from Lua scripts |
1510 | | -#ifdef HAS_INPUT |
1511 | | - m_input_handler.get_events_safe([&](auto& key_set) { |
1512 | | - std::erase_if(key_set.key_data, [&](auto& item) -> bool { |
1513 | | - auto& [_, key_data] = item; |
1514 | | - bool were_all_events_registered_from_lua = true; |
1515 | | - std::erase_if(key_data, [&](Input::KeyData& key_data) -> bool { |
1516 | | - // custom_data == 1: Bind came from Lua, and custom_data2 is a pointer to LuaMod. |
1517 | | - // custom_data == 2: Bind came from C++, and custom_data2 is a pointer to KeyDownEventData. Must free it. |
1518 | | - if (key_data.custom_data == 1) |
1519 | | - { |
1520 | | - return true; |
1521 | | - } |
1522 | | - else |
1523 | | - { |
1524 | | - were_all_events_registered_from_lua = false; |
1525 | | - return false; |
1526 | | - } |
1527 | | - }); |
1528 | | - |
1529 | | - return were_all_events_registered_from_lua; |
1530 | | - }); |
1531 | | - }); |
1532 | | -#endif |
1533 | | - |
1534 | 1596 | // Remove all custom properties |
1535 | 1597 | // Uncomment when custom properties are working |
1536 | 1598 | LuaType::LuaCustomProperty::StaticStorage::property_list.clear(); |
|
0 commit comments