While working on an update for my "Cheat Panel" mod, I needed to create easy to use and universal functions/modules that could work in VLUA and GE. For example, a "setTimeout" module that could call a function "added" to it after a certain time has passed, as implemented in JavaScript. However, the number of modules quickly increased, and I decided to separate them into a separate mod, which could then be used in future projects not only by me, but also by everyone. The idea is very simple - to add superUsefulUtilities (hereinafter - sUU) to your mod, it will be enough to write one line at the beginning of the script file: Spoiler: VLUA Code: if not readFile"sUU"then obj:queueGameEngineLua[[local i,m,s,k,v,t,f="SUU mod id from the repository (not yet known)",core_modmanager,settings,"onlineFeatures","enable",0,""if(m.getModNameFromID(i))then m.activateModId(i)else f = function()if(s.getValue(k)~=v)then s.setValue(k,v)end;if Engine.Online.isAuthenticated()then core_repository.modSubscribe(i)elseif t<25 then t=t+1 getmetatable(newproxy(true)).__gc=f;end;end;f()end]]return{}end Spoiler: GE Code: if not readFile"sUU"then local i,m,s,k,v,t,f="SUU mod id from the repository (not yet known)",core_modmanager,settings,"onlineFeatures","enable",0,""if(m.getModNameFromID(i))then m.activateModId(i)else f = function()if(s.getValue(k)~=v)then s.setValue(k,v)end;if Engine.Online.isAuthenticated()then core_repository.modSubscribe(i)elseif t<25 then t=t+1 getmetatable(newproxy(true)).__gc=f;end;end;f()end;return{}end Spoiler: Unminimized version Spoiler: VLUA Code: if not readFile("sUU") then obj:queueGameEngineLua([[ local id = "SUU mod id from the repository (not yet known)" if core_modmanager.getModNameFromID(id) then core_modmanager.activateModId(id) else local tryCount = 1 local MAX_TRY_COUNT = 25 local function installationTry() if settings.getValue("onlineFeatures") ~= "enable" then settings.setValue("onlineFeatures", "enable") end if Engine.Online.isAuthenticated() and core_modmanager.isReady() then core_repository.modSubscribe(id) elseif tryCount < MAX_TRY_COUNT then tryCount = tryCount + 1 getmetatable(newproxy(true)).__gc = installationTry end end installationTry() end ]]) return {} end Spoiler: GE Code: if not readFile("sUU") then local id = "SUU mod id from the repository (not yet known)" if core_modmanager.getModNameFromID(id) then core_modmanager.activateModId(id) else local tryCount = 1 local MAX_TRY_COUNT = 25 local function installationTry() if settings.getValue("onlineFeatures") ~= "enable" then settings.setValue("onlineFeatures", "enable") end if Engine.Online.isAuthenticated() and core_modmanager.isReady() then core_repository.modSubscribe(id) elseif tryCount < MAX_TRY_COUNT then tryCount = tryCount + 1 getmetatable(newproxy(true)).__gc = installationTry end end installationTry() end return {} end Example of including: If the mod is found - ok, if not - it will be installed. After installation, it will automatically reload GE and VLUA, so your newly loaded mod will be able to use modules from sUU. On July 19, 2022, the first module, setTimeout, was written. For more than a year I worked on the update for the "Сheat Panel" and in parallel created modules for sUU. Mostly, of course, the work was done on sUU. And now, on August 25, 2023 I can say that sUU is finished. Currently, 34.5 modules are completed: trunc numberToBool boolToNumber tableContainsKey tableContainsKeySafe tableContainsKeyCaseInsensitive tableFindKey tableFindKeyCaseInsensitive tableGetElementSafe tableContainsElement tableContainsElementCaseInsensitive tableCopy mergeTables getModByTagId getModTagIdByTagLine disableDefaultAutoUpdate checkUpdate updateMod autoUpdate requireMod installModule onUpdate onUiReady setTimeout skipIterations setInterval createBackgroundTask wrap registerKeyDownCallback registerKeyHoldCallback registerKeyUpCallback getVehRotation getTimeMS simpleToastr Spoiler: 34.5 Spoiler: Unsafe to use. Spoiler: Only use it if you know what you're doing. Spoiler: Memory leaks are likely. functionsStore. Module Description: trunc - trunc function truncates the fractional part. numberToBool - numberToBool function converts numbers to boolean values. boolToNumber - boolToNumber function converts boolean values to numbers. tableContainsKey - tableContainsKey function returns true if the table contains a key, and false if no key is found. tableContainsKeySafe - tableContainsKeySafe function returns true if the table contains a key, and false if no key is found. tableContainsKeySafe function allows you to check the contains key without calling meta-methods. It does not differ from rawget, but you can pass it (nil,nil) and it will not cause an error, unlike rawget. tableContainsKeyCaseInsensitive - tableContainsKeyCaseInsensitive function returns true if the table contains a key, and false if no key is found. Case insensitive. tableFindKey - tableFindKey function searches key by value. tableFindKeyCaseInsensitive - tableFindKeyCaseInsensitive function searches key by value. Case insensitive. tableGetElementSafe - tableGetElementSafe function allows you to get a value without calling meta-methods. It does not differ from rawget, but you can pass it (nil,nil) and it will not cause an error, unlike rawget. tableContainsElement - tableContainsElement function returns true if the table contains an element, and false if the element is not found. tableContainsElementCaseInsensitive - tableContainsElementCaseInsensitive function returns true if the table contains an element, and false if the element is not found. Case insensitive. tableCopy - tableCopy function performs a deep copy of the table. Supports filters. mergeTables - mergeTables function merges tables. Supports filters. getModByTagId - getModByTagId function will return information about the mod by tagid. The mod must be installed in the mods folder. getModTagIdByTagLine - getModTagIdByTagLine function will return the tagId of the mod by tagLine. The mod must be installed in the mods folder. disableDefaultAutoUpdate - disableDefaultAutoUpdate function disables vanilla auto-updates for mods with a tagId. This means that the only way to update the mod would be to use the functions: autoUpdate or updateMod. checkUpdate - checkUpdate function checks for updates on a mod by tagId. You can use tagLine when you don't know the tagId, for example, when you upload a mod to the repository. But after loading it is necessary to specify the tagId. updateMod - updateMod function will update the mod with the tagId. If the mod was not found, it will be installed. autoUpdate - autoUpdate function will automatically update the mod by tagId when updates are released on it. You can use tagLine when you don't know the tagId, for example, when you upload a mod to the repository. But after loading it is necessary to specify the tagId. requireMod - requireMod function will install the mod with tagId if it was not found, or add it to autoUpdate if it was. installModule - installModule function installs modules. onUpdate - onUpdate function adds functions to the list of functions that will be called on each iteration of onUpdate(GE) or updateGFX(VLUA). To remove a function from onUpdate you need to write in that function - onUpdate:clear(). onUiReady - The onUiReady function adds functions to the list that will be called each time the interface is updated. For example - when the user presses F5. To remove a function from onUiReady you need to write in that function - onUiReady:clear(). setTimeout - setTimeout function adds the function to waiting and it will be called after the specified time has passed. skipIterations - skipIterations function adds a function to the waiting and it will be called after the specified number of iterations onUpdate(GE) or updateGFX(VLUA). setInterval - function added to setInterval will be called with the specified periodicity. To remove a function from setInterval you need to write in that function - setInterval:clear(). createBackgroundTask - createBackgroundTask function is needed to create asynchronous, background functions, for example, with the loop of a large array without lags. Yes, the idea is taken from extensions.core_jobsystem, but I think it's a little simpler. You can use the "wrap" module to create a wrap. wrap - wrap function will create a wrapper over the function, and when it is called, the original function with the arguments passed to it will be called as well. registerKeyDownCallback - registerKeyDownCallback function adds your function to a list that will "listen" for key presses. registerKeyHoldCallback - registerKeyHoldCallback function adds your function to a list that will "listen" for key holds. registerKeyUpCallback - registerKeyUpCallback function adds your function to the list that will "listen" for key releases. getVehRotation - getVehRotation function returns the rotation vector(X, Y, Z) in degrees. getVehRotation function can also calculate the rotation for the vehicle passed in the argument, e.g. obj for VLUA and be:getPlayerVehicle(0)(or or any other) for GE. getTimeMS - getTimeMS function returns the current time in ms. simpleToastr - The difference with guihooks.trigger("toastrMsg", ...) is that my module allows the use of lua functions. simpleToastr can be used for example to display a recommendation to install an optional mod (e.g. an add-on). All modules can be used in both VLUA and GE.All modules(except 34.5) are "safe" to use. Memory leaks are excluded. If a function has been stored somewhere, it will be removed when it is no more needed. For example, if a module was called from VLUA that needs to register a callback function in GE, then after reloading or removing the vehicle, the "bound" function in GE will be removed. The same is true in the opposite direction, if the GE is restarted, the function required for it in VLUA will be removed. If you find any error, write about it, I'll try to fix it as soon as possible. Also, all modules (except obviously getVehRotation) can work even if they are loaded before the main GE or VLUA systems. For example, even before "luaCore". Spoiler: Loading before the "game" I will not tell you how to do it, who really need it will understand himself, but just say that in order to make the mod sUU work at this stage, you need to write the following: Spoiler: GE Code: FS:mountList((function() local modName = "superusefulutilities" local path = "/mods/unpacked/" .. modName .. "/" if not FS:directoryExists(path) then local mods = FS:findFiles( "/mods/", "*.zip", -1, false, false ) for _, modPath in pairs(mods) do if modPath:match(modName) then path = modPath break end end end return {{srcPath = path}} end)()) Spoiler: VLUA Nothing needs to be done, everything will work as it is. Answers to some questions: Will the mod be paid - NO. Will the mod be in the repository - YES. Can it be used in your own mods - YES. Can you COPY files/code from the mod - NO. Spoiler Copying files/code is not allowed and is not recommended as the integration is very simple.(see above). In addition, the mod will be updated and fixes/optimizations are possible. P.S. In general, the mod is intended for "programmers" only, and for solving specific problems/issues I've encountered myself. P.S.2 a little later I will add an option to support me.
Not really, all modules come in 1 mod (weight - 95 kb). According to the idea, by writing only 1 line, all of them become available for use. User installs your mod->First run installs sUU->Automatic reload lua->Everything works. I would not want to separate them, as they use each other. And, as I wrote in the post, mod integrity and not allowing copying ensures operability, avoids conflicts, outdates and, as a result, increases stability. Also, there is no need to manually update modules (as opposed to copying individual modules into the mod). About git, yes, I will do it. (but a little later, I'll release the mod first)
I meant "npm" more in the sense that this mod could potentially lead to a new type of mod: lua code libraries, becoming popular. But I do see the logic in your decisions