diff --git a/lib/cache/client/cache.lua b/lib/cache/client/cache.lua index df493c8..b9aefdc 100644 --- a/lib/cache/client/cache.lua +++ b/lib/cache/client/cache.lua @@ -54,10 +54,10 @@ local function GetWeapon() return GetSelectedPedWeapon(ped) end -Cache.Create("Ped", GetPed, 1000) -- Check ped every second (adjust as needed) -Cache.Create("Vehicle", GetVehicle, 500) -- Check vehicle every 500ms -Cache.Create("Seat", GetVehicleSeat, 500) -- Check if in vehicle every 500ms -Cache.Create("Weapon", GetWeapon, 250) -- Check weapon every 250ms -Cache.Create("ServerId", GetServerId, 1000) -- Check server ID every second +Cache.Create("Ped", GetPed, nil) -- Check ped every second (adjust as needed) +Cache.Create("Vehicle", GetVehicle, 500) -- Check vehicle every 500ms +Cache.Create("Seat", GetVehicleSeat, 500) -- Check if in vehicle every 500ms +Cache.Create("Weapon", GetWeapon, 250) -- Check weapon every 250ms +Cache.Create("ServerId", GetServerId, nil) -- Check server ID every second return Cache diff --git a/lib/cache/shared/cache.lua b/lib/cache/shared/cache.lua index ed42a50..c6fe0b1 100644 --- a/lib/cache/shared/cache.lua +++ b/lib/cache/shared/cache.lua @@ -1,29 +1,54 @@ -local Cache = {} +---@class CacheEntry +---@field Name string +---@field Compare fun(): any +---@field WaitTime integer +---@field LastChecked integer|nil +---@field OnChange fun(new:any, old:any)[] +---@field Value any + +---@class CacheModule +---@field Caches table +---@field LoopRunning boolean +---@field Create fun(name:string, compare:fun():any, waitTime?:integer): CacheEntry +---@field Get fun(name:string): any +---@field OnChange fun(name:string, onChange:fun(new:any, old:any)) +---@field Remove fun(name:string) +local Cache = {} ---@type CacheModule + local Config = Require("settings/sharedConfig.lua") local max = 5000 local CreateThread = CreateThread local Wait = Wait local GetGameTimer = GetGameTimer - Cache.Caches = Cache.Caches or {} Cache.LoopRunning = Cache.LoopRunning or false -local function print(...) +---@param ... any +local function debugPrint(...) if Config.DebugLevel == 0 then return end print("^2[Cache]^0", ...) end +local function HasActiveCaches() + for _, cache in pairs(Cache.Caches) do + if cache.WaitTime ~= nil then + return true + end + end + return false +end + local function StartLoop() if Cache.LoopRunning then return end + if not HasActiveCaches() then return end Cache.LoopRunning = true CreateThread(function() while Cache.LoopRunning do local now = GetGameTimer() local minWait = nil - local caches = Cache.Caches - for name, cache in pairs(caches) do - if cache.Compare then + for name, cache in pairs(Cache.Caches) do + if cache.Compare and cache.WaitTime ~= nil then cache.LastChecked = cache.LastChecked or now cache.WaitTime = cache.WaitTime or max local elapsed = now - cache.LastChecked @@ -32,8 +57,8 @@ local function StartLoop() local oldValue = cache.Value cache.Value = cache.Compare() if cache.Value ~= oldValue and cache.OnChange then - for _, onChange in pairs(cache.OnChange) do - onChange(cache.Value, oldValue) + for i = 1, #cache.OnChange do + cache.OnChange[i](cache.Value, oldValue) end end cache.LastChecked = now @@ -50,44 +75,53 @@ local function StartLoop() else Wait(max) end + -- if there is no cache valid, stop the loop + if not HasActiveCaches() then + Cache.LoopRunning = false + end end end) end +---@param name string +---@param compare fun():any +---@param waitTime? integer +---@return CacheEntry function Cache.Create(name, compare, waitTime) assert(name, "Cache name is required.") assert(compare, "Comparison function is required.") assert(type(compare) == "function", "Comparison function must be a function.") - local _name = tostring(name) -- Ensure name is a string + local _name = tostring(name) local cache = Cache.Caches[_name] if cache and cache.Compare == compare then - print(name .. " already exists with the same comparison function.") + debugPrint(_name .. " already exists with the same comparison function.") return cache end - cache = nil -- clean variable, so we can use the same name for the new cache - local s, r = pcall(function() -- Try to create the cache and catch any errors - local _cache = { - Name = _name, - Compare = compare, - WaitTime = waitTime or max, - LastChecked = nil, - OnChange = {}, - Value = compare() - } - Cache.Caches[_name] = _cache - print(_name .. " created with initial value: " .. tostring(Cache.Caches[_name].Value)) - for _, onChange in pairs(_cache.OnChange) do - onChange(_cache.Value, nil) - end - end) - if not s then - print("Error creating cache '" .. _name .. "': " .. tostring(r)) + local ok, result = pcall(compare) + if not ok then + debugPrint("Error creating cache '" .. _name .. "': " .. tostring(result)) return nil end - - return StartLoop() + ---@type CacheEntry + local newCache = { + Name = _name, + Compare = compare, + WaitTime = waitTime, -- can be nil + LastChecked = nil, + OnChange = {}, + Value = result + } + Cache.Caches[_name] = newCache + debugPrint(_name .. " created with initial value: " .. tostring(result)) + for i = 1, #newCache.OnChange do + newCache.OnChange[i](newCache.Value, nil) + end + StartLoop() + return newCache end +---@param name string +---@return any function Cache.Get(name) assert(name, "Cache name is required.") local _name = tostring(name) @@ -95,25 +129,44 @@ function Cache.Get(name) return cache and cache.Value or nil end +---@param name string +---@param onChange fun(new:any, old:any) function Cache.OnChange(name, onChange) assert(name, "Cache name is required.") - local _name = tostring(name) -- Ensure name is a string + local _name = tostring(name) local cache = Cache.Caches[_name] assert(cache, "Cache with name '" .. _name .. "' does not exist.") cache.OnChange[#cache.OnChange + 1] = onChange end +---@param name string function Cache.Remove(name) assert(name, "Cache name is required.") - local _name = tostring(name) -- Ensure name is a string + local _name = tostring(name) local cache = Cache.Caches[_name] if cache then Cache.Caches[_name] = nil - print(_name .. " removed from cache.") + debugPrint(_name .. " removed from cache.") if next(Cache.Caches) == nil then - Cache.LoopRunning = false -- Stop the loop if no caches are left + Cache.LoopRunning = false end end end +setmetatable(Cache, { + __index = function(self, key) + local cache = self.Caches[key] + if cache then + return cache.Value + else + return rawget(self, key) + end + end, + __call = function(self, key) + return self.Get(self, key) + end, +}) + +-- with this change now we can use Cache("name") to get the value or simple Cache.name (Cache.Get("name")) + return Cache