Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
601e1aa
Create Freeshop.cpp
Pyxalon Oct 6, 2026
a1bfc1a
Update Recovery.cpp
Pyxalon Oct 6, 2026
cfe1859
Merge branch 'CSY0N:main' into main
Pyxalon Oct 6, 2026
813b9d9
Create CatalogCacheListener.hpp
Pyxalon Oct 8, 2026
661cc99
Update ApartmentHeist.cpp
Pyxalon Oct 8, 2026
03085f5
Update CayoPericoHeist.cpp
Pyxalon Oct 8, 2026
29fe190
Remove commented code for group registration
Pyxalon Oct 8, 2026
eed7c90
Update CayoPericoHeist.cpp
Pyxalon Oct 8, 2026
82acfca
Update ApartmentHeist.cpp
Pyxalon Oct 8, 2026
dc62b91
Update netCatalogBaseItem.hpp
Pyxalon Oct 8, 2026
86e8fc8
Refactor CatalogCacheListener and netCatalog structures
Pyxalon Oct 8, 2026
c808cec
Update Freeshop.cpp
Pyxalon Oct 8, 2026
20eab57
Update FileMgr.hpp
Pyxalon Oct 8, 2026
54aaaf1
Update Default.cpp
Pyxalon Oct 8, 2026
b4acc02
Update ImGui.cpp
Pyxalon Oct 8, 2026
668e32e
Update Menu.cpp
Pyxalon Oct 8, 2026
f7ea1e0
Add RequireGameBuild function to Script.cpp
Pyxalon Oct 8, 2026
2558bbc
Update Util.cpp
Pyxalon Oct 8, 2026
7fbce15
Refactor script loading with LoadRequest structure
Pyxalon Oct 8, 2026
a6ca6c2
Update LuaManager.cpp
Pyxalon Oct 8, 2026
098dd97
Create LuaRequire.cpp
Pyxalon Oct 8, 2026
03d0e88
Create LuaRequire.hpp
Pyxalon Oct 8, 2026
b8fcbfe
Update LuaScript.hpp
Pyxalon Oct 8, 2026
4a0eb57
Update LuaScripts.cpp
Pyxalon Oct 8, 2026
8442592
Update Event.cpp
Pyxalon Oct 8, 2026
56434ee
Rename Network class to LuaNetwork and add JoinSession
Pyxalon Oct 8, 2026
478a28c
Fix index for item quantity in Transactions.cpp
Pyxalon Oct 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/core/filemgr/FileMgr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace YimMenu
FileMgr() = default;

public:
virtual ~FileMgr() = default;
~FileMgr() = default;

FileMgr(const FileMgr&) = delete;
FileMgr(FileMgr&&) noexcept = delete;
Expand Down Expand Up @@ -48,4 +48,4 @@ namespace YimMenu
private:
std::filesystem::path m_RootFolder;
};
}
}
101 changes: 71 additions & 30 deletions src/core/scripting/LuaManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace YimMenu
{
void LuaManager::AddUnloadedScript(std::string_view name, std::string_view path)
{
if (IsIncludeScript(std::filesystem::path(path)))
return;
m_UnloadedScripts.push_back({std::string(name), std::string(path)});
}

Expand Down Expand Up @@ -41,22 +43,26 @@ namespace YimMenu

void LuaManager::LoadScriptImpl(std::string path)
{
if (std::filesystem::path(path).extension() != ".lua" || IsIncludeScript(std::filesystem::path(path)))
return;
std::lock_guard lock(m_LoadMutex);
m_ScriptsToLoad.push(path);
m_ScriptsToLoad.push({std::move(path), true});
}

void LuaManager::DisableScriptImpl(std::string path)
{
std::lock_guard lock(m_LoadMutex);
if (std::find(m_ScriptsToDisable.begin(), m_ScriptsToDisable.end(), path) == m_ScriptsToDisable.end())
m_ScriptsToDisable.push_back(std::move(path));
}

void LuaManager::RunScriptImpl()
{
m_MainThreadId = GetCurrentThreadId();

auto scripts_dir = FileMgr::GetProjectFolder("./scripts");
for (const auto& entry : std::filesystem::directory_iterator(scripts_dir.Path()))
{
if (entry.path().extension() != ".lua")
continue;

m_ScriptsToLoad.push(entry.path().string());
}
for (const auto& script : DiscoverScripts(scripts_dir.Path(), false))
m_ScriptsToLoad.push({script.string(), false});

while (g_Running)
{
Expand All @@ -65,14 +71,59 @@ namespace YimMenu
std::lock_guard lock(m_LoadMutex);
while (!m_ScriptsToLoad.empty())
{
std::erase_if(m_UnloadedScripts, [this](auto& script) {
auto request = std::move(m_ScriptsToLoad.front());
m_ScriptsToLoad.pop();
auto path = request.m_Path;
if (IsIncludeScript(std::filesystem::path(path)))
continue;
if (std::ranges::any_of(m_LoadedScripts, [&path](const auto& script) {
std::error_code ec;
return std::filesystem::equivalent(path, script->GetPath(), ec);
}))
continue;
if (request.m_Enable)
{
try
{
path = MoveScriptFile(path, scripts_dir.Path(), true).string();
}
catch (const std::filesystem::filesystem_error& error)
{
Notifications::Show("Lua Scripts", error.what(), NotificationType::Error);
continue;
}
}
std::erase_if(m_UnloadedScripts, [&path, &request](auto& script) {
std::error_code ec;
return std::filesystem::equivalent(m_ScriptsToLoad.front(), script.m_Path, ec);
return script.m_Path == request.m_Path || std::filesystem::equivalent(path, script.m_Path, ec);
});
m_LoadedScripts.push_back(std::make_shared<LuaScript>(m_ScriptsToLoad.front()));
m_ScriptsToLoad.pop();
m_LoadedScripts.push_back(std::make_shared<LuaScript>(path));
}

std::erase_if(m_ScriptsToDisable, [this, &scripts_dir](const std::string& path) {
auto found = std::ranges::find_if(m_LoadedScripts, [&path](const auto& script) {
return script->GetPath() == path;
});
if (found == m_LoadedScripts.end())
return true;
auto& script = *found;
if (!script->IsRunning())
return true;
if (!script->SafeToUnload())
return false;
try
{
auto destination = MoveScriptFile(std::filesystem::path(path), scripts_dir.Path(), false);
script->SetPath(destination.string());
script->Unload();
}
catch (const std::filesystem::filesystem_error& error)
{
Notifications::Show("Lua Scripts", error.what(), NotificationType::Error);
}
return true;
});

// 2) remove scripts if needed
std::erase_if(m_LoadedScripts, [this](auto& script) {
if (!script->SafeToUnload())
Expand All @@ -91,7 +142,7 @@ namespace YimMenu
}
else if (script->GetLoadState() == LuaScript::LoadState::WANT_RELOAD)
{
m_ScriptsToLoad.push(std::string(script->GetPath())); // will be reloaded next tick
m_ScriptsToLoad.push({std::string(script->GetPath()), false}); // will be reloaded next tick
unload = true;
}

Expand All @@ -108,24 +159,14 @@ namespace YimMenu
if (m_LastRefreshedUnloadedScripts + 10s < std::chrono::system_clock::now())
{
m_UnloadedScripts.clear();
for (const auto& entry : std::filesystem::directory_iterator(scripts_dir.Path()))
for (const auto& path : DiscoverScripts(scripts_dir.Path()))
{
if (entry.path().extension() != ".lua")
if (std::ranges::any_of(m_LoadedScripts, [&path](const auto& script) {
std::error_code ec;
return std::filesystem::equivalent(script->GetPath(), path, ec);
}))
continue;

for (auto& script : m_LoadedScripts)
{
std::error_code ec;
if (std::filesystem::equivalent(script->GetPath(), entry.path().string(), ec))
{
// continue;
goto next;
}
}

AddUnloadedScript(entry.path().filename().string(), entry.path().string());
next:
continue;
AddUnloadedScript(path.filename().string(), path.string());
}
m_LastRefreshedUnloadedScripts = std::chrono::system_clock::now();
}
Expand Down Expand Up @@ -220,4 +261,4 @@ namespace YimMenu
if (locked)
_type->Unlock();
}
}
}
88 changes: 86 additions & 2 deletions src/core/scripting/LuaManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ namespace YimMenu
using ForAllResourcesOfTypeCallback = void(*)(LuaResource* resource);

private:
struct LoadRequest
{
std::string m_Path;
bool m_Enable;
};

std::vector<std::shared_ptr<LuaScript>> m_LoadedScripts;
std::vector<UnloadedScript> m_UnloadedScripts;
std::chrono::system_clock::time_point m_LastRefreshedUnloadedScripts;
std::vector<LuaLibrary*> m_Libraries;
std::vector<LuaResourceType*> m_ResourceTypes;
std::queue<std::string> m_ScriptsToLoad;
std::queue<LoadRequest> m_ScriptsToLoad;
std::vector<std::string> m_ScriptsToDisable;
std::mutex m_LoadMutex;
std::uint32_t m_MainThreadId;
lua_State* m_RunningCoroutine = nullptr;
Expand All @@ -39,6 +46,7 @@ namespace YimMenu
LuaResourceType* GetResourceTypeImpl(int index);
void LoadLibrariesImpl(lua_State* state);
void LoadScriptImpl(std::string path);
void DisableScriptImpl(std::string path);
void RunScriptImpl();
bool IsRunningInMainThreadImpl();
void SetRunningCoroutineImpl(lua_State* script);
Expand All @@ -54,6 +62,77 @@ namespace YimMenu
}

public:
static bool IsIncludeScript(const std::filesystem::path& path)
{
return path.parent_path().filename().string().contains("include");
}

static std::filesystem::path MoveScriptFile(const std::filesystem::path& path, const std::filesystem::path& scripts_folder, bool enable)
{
namespace fs = std::filesystem;
auto root = fs::canonical(scripts_folder);
auto source = fs::canonical(path);
auto relative = source.lexically_relative(root);
if (relative.empty() || relative.has_root_path() || *relative.begin() == ".." || source.extension() != ".lua" || IsIncludeScript(source) || !fs::is_regular_file(source))
throw fs::filesystem_error("script is outside the scripts directory or is an include file", source, std::make_error_code(std::errc::invalid_argument));
if (*relative.begin() == "disabled")
relative = relative.lexically_relative("disabled");
if (enable && std::ranges::any_of(relative.parent_path(), [](const fs::path& component) {
return component.string().contains("disabled");
}))
relative = source.filename();
auto destination = (enable ? root : root / "disabled") / relative;
if (source == destination)
return source;
if (fs::weakly_canonical(destination.parent_path()) != destination.parent_path())
throw fs::filesystem_error("script destination must not be a linked directory", destination, std::make_error_code(std::errc::invalid_argument));
if (fs::exists(fs::symlink_status(destination)))
throw fs::filesystem_error("another script already has this name", source, destination, std::make_error_code(std::errc::file_exists));
fs::create_directories(destination.parent_path());
fs::rename(source, destination);
return destination;
}

static std::vector<std::filesystem::path> DiscoverScripts(const std::filesystem::path& scripts_folder, bool include_disabled = true)
{
namespace fs = std::filesystem;
std::vector<fs::path> scripts;
std::error_code ec;
auto root = fs::canonical(scripts_folder, ec);
if (ec)
return scripts;
auto disabled_folder = [&root](const fs::path& path) {
auto relative = path.lexically_relative(root);
return std::ranges::any_of(relative, [](const fs::path& component) {
return component.string().contains("disabled");
});
};

for (fs::recursive_directory_iterator entry(root, fs::directory_options::skip_permission_denied, ec), end; entry != end; entry.increment(ec))
{
if (entry->is_directory(ec))
{
auto folder = fs::canonical(entry->path(), ec);
if (ec || folder != entry->path() || (!include_disabled && disabled_folder(folder)))
entry.disable_recursion_pending();
continue;
}
if (entry->path().extension() != ".lua" || IsIncludeScript(entry->path()) || !entry->is_regular_file(ec))
continue;

auto file = fs::canonical(entry->path(), ec);
if (ec || IsIncludeScript(file) || (!include_disabled && disabled_folder(file.parent_path())))
continue;
auto relative = file.lexically_relative(root);
if (relative.empty() || relative.has_root_path() || *relative.begin() == "..")
continue;
scripts.push_back(std::move(file));
}
std::sort(scripts.begin(), scripts.end());
scripts.erase(std::unique(scripts.begin(), scripts.end()), scripts.end());
return scripts;
}

static void RegisterLibrary(LuaLibrary* library)
{
GetInstance().RegisterLibraryImpl(library);
Expand Down Expand Up @@ -84,6 +163,11 @@ namespace YimMenu
GetInstance().LoadScriptImpl(file_name);
}

static void DisableScript(std::string file_name)
{
GetInstance().DisableScriptImpl(std::move(file_name));
}

static void RunScript()
{
GetInstance().RunScriptImpl();
Expand Down Expand Up @@ -121,4 +205,4 @@ namespace YimMenu
return GetInstance().DispatchEventImpl(event, add_arguments_cb, handle_result);
}
};
}
}
Loading
Loading