[lvs_framework] addons/lvs_framework/lua/autorun/lvs_init.lua:137: attempt to index upvalue 'self' (a nil value)
  1. onsuccess - addons/lvs_framework/lua/autorun/lvs_init.lua:137
   2. unknown - lua/includes/modules/http.lua:29


[lvs_framework] addons/lvs_framework/lua/autorun/lvs_init.lua:137: attempt to index upvalue 'self' (a nil value)
  1. onsuccess - addons/lvs_framework/lua/autorun/lvs_init.lua:137
   2. unknown - lua/includes/modules/http.lua:29

```lua
-- ======================================
-- LVS Framework Initialization Script
-- ======================================

-- Initialize the LVS table if it doesn't already exist
LVS = istable(LVS) and LVS or {}

-- ========================
-- Constants and Settings
-- ========================
LVS.VERSION = 301
LVS.VERSION_GITHUB = 0
LVS.VERSION_TYPE = ".WS"
LVS.VERSION_ADDONS_OUTDATED = false

LVS.KEYS_CATEGORIES = {}
LVS.KEYS_REGISTERED = {}

-- Key mappings for quick access
LVS.pSwitchKeys = {
    [KEY_1] = 1, [KEY_2] = 2, [KEY_3] = 3, [KEY_4] = 4,
    [KEY_5] = 5, [KEY_6] = 6, [KEY_7] = 7, [KEY_8] = 8,
    [KEY_9] = 9, [KEY_0] = 10
}

LVS.pSwitchKeysInv = {
    [1] = KEY_1, [2] = KEY_2, [3] = KEY_3, [4] = KEY_4,
    [5] = KEY_5, [6] = KEY_6, [7] = KEY_7, [8] = KEY_8,
    [9] = KEY_9, [10] = KEY_0
}

-- Theme color for LVS UI elements
LVS.ThemeColor = Color(127, 0, 0, 255)

-- Wheel steering types
LVS.WHEEL_BRAKE = 1
LVS.WHEEL_STEER_NONE = 2
LVS.WHEEL_STEER_FRONT = 3
LVS.WHEEL_STEER_REAR = 4

-- =======================
-- Weapon Presets
-- =======================
LVS.WEAPONS = {
    ["DEFAULT"] = {
        Icon = Material("lvs/weapons/bullet.png"),
        Ammo = 9999,
        Delay = 0,
        HeatRateUp = 0.2,
        HeatRateDown = 0.25,
        Attack = function(ent) end,
        StartAttack = function(ent) end,
        FinishAttack = function(ent) end,
        OnSelect = function(ent, old) end,
        OnDeselect = function(ent, new) end,
        OnThink = function(ent, active) end,
        OnOverheat = function(ent) end,
        OnRemove = function(ent) end,
    },
}

-- ===========
-- Localization
-- ===========
local tableInsert = table.insert
local tableCopy = table.Copy
local stringMatch = string.match
local stringReplace = string.Replace
local stringStartsWith = string.StartsWith
local stringExplode = string.Explode
local ipairs = ipairs
local pairs = pairs
local tonumber = tonumber
local isstring = isstring
local istable = istable
local isfunction = isfunction

-- ====================
-- Version Handling
-- ====================
function LVS:GetVersion()
    return self.VERSION
end

function LVS:AddWeaponPreset(name, data)
    if not isstring(name) or not istable(data) then
        print("[LVS] AddWeaponPreset failed: 'name' must be a string and 'data' must be a table.")
        return
    end
    self.WEAPONS[name] = data
end

function LVS:GetWeaponPreset(name)
    return self.WEAPONS[name] and tableCopy(self.WEAPONS[name]) or tableCopy(self.WEAPONS["DEFAULT"])
end

-- ====================
-- Key Registration
-- ====================
function LVS:AddKey(name, category, printname, cmd, default)
    local data = {
        printname = printname,
        id = name,
        category = category,
        cmd = cmd,
    }

    -- Ensure the category table exists
    self.KEYS_CATEGORIES[category] = self.KEYS_CATEGORIES[category] or {}

    if SERVER then
        -- Server-side: Register the key without default value
        tableInsert(self.KEYS_REGISTERED, data)
    else
        -- Client-side: Handle default value and create a client convar
        data.default = self:_ProcessDefaultKey(default)
        tableInsert(self.KEYS_REGISTERED, data)
        CreateClientConVar(cmd, data.default, true, true)
    end
end

-- Helper function to process default key values
function LVS:_ProcessDefaultKey(default)
    if default and isstring(default) then
        local Key = input.LookupBinding(default)
        return Key and input.GetKeyCode(Key) or 0
    end
    return 0
end

-- ====================
-- Update Checking
-- ====================
function LVS:CheckUpdates()
    -- Fetch the latest LVS version from GitHub
    http.Fetch("https://raw.githubusercontent.com/SpaxscE/lvs_base/main/lua/autorun/lvs_init.lua", function(contents, size)
        self.VERSION_GITHUB = self:_ExtractVersion(contents)

        self:_HandleMainVersionCheck()

        self:_CheckAddonsUpdates()
    end, function(error)
        print("[LVS] - Framework: Failed to fetch update information. Error: " .. tostring(error))
    end)
end

-- Extract version number from fetched content
function LVS:_ExtractVersion(contents)
    local entry = stringMatch(contents, "LVS.VERSION%s*=%s*(%d+)")
    return entry and tonumber(entry) or 0
end

-- Handle the main version comparison and notifications
function LVS:_HandleMainVersionCheck()
    if self.VERSION_GITHUB == 0 then
        print("[LVS] - Framework: Latest version could not be detected. You have Version: " .. self:GetVersion())
    else
        if self:GetVersion() >= self.VERSION_GITHUB then
            print("[LVS] - Framework is up to date. Version: " .. self:GetVersion())
        else
            print("[LVS] - Framework: A newer version is available! Version: " .. self.VERSION_GITHUB .. ", You have Version: " .. self:GetVersion())

            if self.VERSION_TYPE == ".GIT" then
                print("[LVS] - Framework: Get the latest version at https://github.com/SpaxscE/lvs_base")
            else
                print("[LVS] - Framework: Restart your game/server to get the latest version!")
            end

            if CLIENT then
                timer.Simple(18, function()
                    chat.AddText(Color(255, 0, 0), "[LVS] - Framework: A newer version is available!")
                end)
            end
        end
    end
end

-- Check updates for all installed addons
function LVS:_CheckAddonsUpdates()
    local addons = file.Find("data_static/lvs/*", "GAME")
    local Delay = 0

    for _, addonFile in ipairs(addons) do
        local addonInfo = file.Read("data_static/lvs/" .. addonFile, "GAME")
        if not addonInfo then continue end

        local addonData = self:_ParseAddonInfo(addonFile, addonInfo)
        if not addonData then continue end

        Delay = Delay + 1.5
        self:_ScheduleAddonUpdateCheck(addonData, Delay)
    end
end

-- Parse addon information from its info file
function LVS:_ParseAddonInfo(addonFile, addonInfo)
    local data = stringExplode("\n", addonInfo)
    local wsid = stringReplace(addonFile, ".txt", "")
    local addonName, addonURL, addonVersion = wsid, nil, nil

    for _, entry in ipairs(data) do
        if stringStartsWith(entry, "url=") then
            addonURL = stringReplace(entry, "url=", "")
        elseif stringStartsWith(entry, "version=") then
            addonVersion = tonumber(stringReplace(entry, "version=", "")) or 0
        elseif stringStartsWith(entry, "name=") then
            addonName = stringReplace(entry, "name=", "")
        end
    end

    if not addonURL or not addonVersion then
        print("[LVS] - Addon Info Missing: " .. addonFile)
        return nil
    end

    return {
        name = addonName,
        url = addonURL,
        version = addonVersion,
        wsid = wsid
    }
end

-- Schedule an asynchronous update check for an addon
function LVS:_ScheduleAddonUpdateCheck(addonData, delay)
    timer.Simple(delay, function()
        http.Fetch(addonData.url, function(content, size)
            local latestVersion = self:_ExtractAddonVersion(content)
            self:_HandleAddonVersionCheck(addonData, latestVersion)
        end, function(error)
            print("[LVS] - Addon Update Check Failed for " .. addonData.name .. ". Error: " .. tostring(error))
        end)
    end)
end

-- Extract addon version from fetched content
function LVS:_ExtractAddonVersion(content)
    local entry = stringMatch(content, "version%s*=%s*(%d+)")
    return entry and tonumber(entry) or 0
end

-- Handle the addon version comparison and notifications
function LVS:_HandleAddonVersionCheck(addonData, latestVersion)
    local wsurl = "https://steamcommunity.com/sharedfiles/filedetails/?id=" .. addonData.wsid

    if latestVersion == 0 then
        print("[LVS] - Latest version of " .. addonData.name .. " (" .. wsurl .. ") could not be detected. You have Version: " .. addonData.version)
    elseif latestVersion > addonData.version then
        print("[LVS] - " .. addonData.name .. " (" .. wsurl .. ") is out of date!")

        if CLIENT then
            timer.Simple(18, function()
                chat.AddText(Color(255, 0, 0), "[LVS] - " .. addonData.name .. " is out of date!")
            end)
        end

        self.VERSION_ADDONS_OUTDATED = true
    else
        print("[LVS] - " .. addonData.name .. " is up to date. Version: " .. addonData.version)
    end
end

-- ====================
-- Vehicle Type Retrieval
-- ====================
function LVS:GetVehicleTypes()
    local VehicleTypes = {}
    local scriptedList = scripted_ents.GetList()

    for _, v in pairs(scriptedList) do
        if not v.t or not isfunction(v.t.GetVehicleType) then continue end

        local vehicletype = v.t:GetVehicleType()
        if not isstring(vehicletype) or stringStartsWith(vehicletype, "LBase") or table.HasValue(VehicleTypes, vehicletype) then
            continue
        end

        tableInsert(VehicleTypes, vehicletype)
    end

    return VehicleTypes
end

-- ====================
-- Include and AddCSLuaFile
-- ====================
AddCSLuaFile("lvs_framework/init.lua")
include("lvs_framework/init.lua")
```