spacebuild/spacebuild

View on GitHub
lua/autorun/server/sv_caf_autostart.lua

Summary

Maintainability
Test Coverage
local gmod_version_required = 145;
if ( VERSION < gmod_version_required ) then
    error("CAF: Your gmod is out of date: found version ", VERSION, "required ", gmod_version_required)
end

local net = net

local net_pools = {"CAF_Addon_Construct", "CAF_Addon_Destruct", "CAF_Start_true", "CAF_Start_false", "CAF_Addon_POPUP"};
for _, v in pairs(net_pools) do
    print("Pooling ", v, " for net library");
    util.AddNetworkString(v)
end

-- Variable Declarations
local CAF2 = {}
CAF = CAF2;
local CAF3 = {}
CAF2.CAF3 = CAF3;

CAF2.StartingUp = false;

local DEBUG = true
CAF3.DEBUG = DEBUG;
local Addons = {}
CAF3.Addons = Addons

local addonlevel = {}
CAF3.addonlevel = addonlevel
addonlevel[1] = {}
addonlevel[2] = {}
addonlevel[3] = {}
addonlevel[4] = {}
addonlevel[5] = {}

local hooks = {}
CAF3.hooks = hooks;
hooks["think"] = {}
hooks["think2"] = {}
hooks["think3"] = {}
hooks["OnEntitySpawn"] = {}
hooks["OnAddonDestruct"] = {}
hooks["OnAddonConstruct"] = {}
hooks["OnAddonExtraOptionChange"] = {}
hooks["TOOL_Allow_Entity_Spawn"] = {}

function CAF2.AllowSpawn(type, sub_type, class, model)
    for k , v in pairs(hooks["TOOL_Allow_Entity_Spawn"]) do
        local ok, err = pcall(type, sub_type, class, model)
        if not (ok) then
            CAF2.WriteToDebugFile("CAF_Hooks", "TOOL_Allow_Entity_Spawn Error: " .. err .."\n")
        else
            if err == true then
                return true;
            elseif err == false then
                return false;
            end
        end
    end
    return true
end


local function ErrorOffStuff(String)
    Msg("----------------------------------------------------------------------\n")
    Msg("-----------Custom Addon Management Framework Error----------\n")
    Msg("----------------------------------------------------------------------\n")
    Msg(tostring(String).."\n")
end

AddCSLuaFile("autorun/client/cl_caf_autostart.lua")
CAF2.CAF3 = CAF3;
include("caf/core/shared/sh_general_caf.lua")
CAF2.CAF3 = nil;

if (not sql.TableExists("CAF_AddonStatus")) then

    sql.Query("CREATE TABLE IF NOT EXISTS CAF_AddonStatus ( id VARCHAR(50) PRIMARY KEY , status TINYINT(1));")

end

--function Declarations

--Local functions

local function UpdateAddonStatus(addon, status)
    if not addon or not status then return false, "Missing parameter(s)" end
    local id = sql.SQLStr(addon)
    local stat = sql.SQLStr(status)
    sql.Query("UPDATE CAF_AddonStatus SET status="..stat.." WHERE id="..id..";")
end

local function SaveAddonStatus(addon, status)
    if not addon or not status then return false, "Missing parameter(s)" end
    local id = sql.SQLStr(addon)
    local stat = sql.SQLStr(status)
    local data = sql.Query("INSERT INTO CAF_AddonStatus(id, status) VALUES("..id..", "..stat..");")
    if data then 
        Msg("Error making a profile for "..ply:Nick().."\n"..data.."\n") 
    end
end

local function LoadAddonStatus( addon, defaultstatus )
    if not addon then return false, "No Addon Given" end
    local id = sql.SQLStr(addon)
    local data = sql.Query("SELECT * FROM CAF_AddonStatus WHERE id = "..id..";")
    if defaultstatus == nil then
        defaultstatus = 1;
    else
        if defaultstatus then
            defaultstatus = 1
        else
            defaultstatus = 0
        end
    end
    if (not data) then
        SaveAddonStatus(addon, defaultstatus)
    else
        return util.tobool(data[1]["status"])
    end
    return util.tobool(defaultstatus);
end

local function OnEntitySpawn(ent , enttype , ply)
    if IsValid(ent) ~= true then
        return
    end
    ent.caf = ent.caf or {}
    ent.caf.custom = ent.caf.custom or {}
    if ent.caf.custom.canreceivedamage == nil then
        ent.caf.custom.canreceivedamage = true
    end
    if ent.caf.custom.canreceiveheatdamage == nil then
        ent.caf.custom.canreceiveheatdamage = true
    end
    for k , v in pairs(hooks["OnEntitySpawn"]) do
        local ok, err = pcall(v, ent , enttype , ply)
        if not (ok) then
            CAF2.WriteToDebugFile("CAF_Hooks", "OnEntitySpawn Error: " .. err .."\n")
        end
    end
end

local function  OnAddonDestruct(name)
    if not name then return end
    net.Start("CAF_Addon_Destruct")
        net.WriteString(name)
    net.Broadcast()
    if not CAF2.StartingUp then
        for k , v in pairs(hooks["OnAddonDestruct"]) do
            local ok, err = pcall(v, name)
            if not (ok) then
                CAF2.WriteToDebugFile("CAF_Hooks", "OnAddonDestruct Error: " .. err .. "\n")
            end
        end
    end
end

local function OnAddonConstruct(name)
    if not name then return end
    net.Start("CAF_Addon_Construct")
        net.WriteString(name)
    net.Broadcast()
    if not CAF2.StartingUp then
        for k , v in pairs(hooks["OnAddonConstruct"]) do
            local ok, err = pcall(v, name)
            if not (ok) then
                CAF2.WriteToDebugFile("CAF_Hooks", "OnAddonConstruct Error: " .. err .. "\n")
            end
        end
    end
end

local function OnAddonExtraOptionChange(AddonName, OptionName, NewStatus)
    if not AddonName or not OptionName then return nil, "Missing Argument" end
    for k , v in pairs(hooks["OnAddonExtraOptionChange"]) do
        local ok, err = pcall(v, AddonName, OptionName, NewStatus)
        if not (ok) then
            CAF2.WriteToDebugFile("CAF_Hooks", "OnAddonExtraOptionChange Error: " .. err .. "\n")
        end
    end
end

--Gmod Spawn Hooks

local function SpawnedSent( ply , ent )
    --Msg("Sent Spawned\n")
    OnEntitySpawn(ent , "SENT" , ply)
end

local function SpawnedVehicle( ply , ent)
    --Msg("Vehicle Spawned\n")
    OnEntitySpawn(ent , "VEHICLE" , ply)
end    

local function SpawnedEnt( ply , model , ent )
    --Msg("Prop Spawned\n")
    OnEntitySpawn(ent , "PROP" , ply)
end

local function PlayerSpawn(ply)
    --Msg("Prop Spawned\n")
    OnEntitySpawn(ply , "PLAYER" , ply)
end

local function NPCSpawn(ply, ent)
    --Msg("Prop Spawned\n")
    OnEntitySpawn(ent , "NPC" , ply)
end
hook.Add( "PlayerSpawnedNPC", "CAF NPC Spawn", NPCSpawn )
hook.Add( "PlayerInitialSpawn", "CAF PLAYER Spawn", PlayerSpawn )
hook.Add( "PlayerSpawnedProp", "CAF PROP Spawn", SpawnedEnt )
hook.Add( "PlayerSpawnedSENT", "CAF SENT Spawn", SpawnedSent )
hook.Add( "PlayerSpawnedVehicle", "CAF VEHICLE Spawn", SpawnedVehicle )

--Global function
--[[

]]

--[[
    WriteToDebugFile
    This function will write the selected message to 
        1) the console
        2) the specified file into the CAF_DEBUG/Server/ folder
            If the file doesn't exist it will be created
]]
function CAF2.WriteToDebugFile(filename, message)
    if not filename or not message then return nil , "Missing Argument" end
    if DEBUG then
        ErrorNoHalt("Filename: "..tostring(filename)..", Message: "..tostring(message).."\n")
    end
    local contents = file.Read("CAF_Debug/server/"..filename..".txt")
    contents = contents or "" 
    contents = contents .. message
    file.Write("CAF_Debug/server/"..filename..".txt", contents)
end

--[[
    ClearDebugFile
        This function will clear the given file in the debug folder
        It will return the content that was in the file before it got cleared
]]
function CAF2.ClearDebugFile(filename)
    if not filename then return nil , "Missing Argument" end
    local contents = file.Read("CAF_Debug/server/"..filename..".txt")
    contents = contents or "" 
    file.Write("CAF_Debug/server/"..filename..".txt", "")
    return content
end

--[[
    GetSavedAddonStatus
        This function will return the the status that was stored in the SQL file last time to make it easier so admins won't need to disable Addons again every time.
]]
function CAF2.GetSavedAddonStatus( addon, defaultstatus )
    return LoadAddonStatus( addon , defaultstatus)
end


--[[
    Start
        This function loads all the Custom Addons on Startup
]]
function CAF2.Start()
    Msg("Starting CAF Addons\n");
    CAF2.StartingUp = true
    net.Start("CAF_Start_true")
    net.Broadcast()
    CAF2.AddServerTag("CAF")
    for level, tab in pairs(addonlevel) do
        print("Loading Level "..tostring(level).." Addons\n")
        for k, v in pairs(tab) do
            if Addons[v] then
                print("-->", "Loading addon "..tostring(v).."\n")
                if Addons[v].AddResourcesToSend then
                    local ok, err = pcall(Addons[v].AddResourcesToSend)
                    if not ok then
                        CAF2.WriteToDebugFile("CAF_ResourceSend", "AddResourcesToSend Error: " .. err .. "\n")
                    end
                end
                if Addons[v].GetStatus  and not Addons[v].GetStatus() then
                    local ok = true
                    if Addons[v].GetRequiredAddons and Addons[v].GetRequiredAddons() then
                        for l, w in pairs(Addons[v].GetRequiredAddons()) do
                            if not Addons[w] then
                                ok = false
                            end
                        end
                    end
                    if ok then
                        local state = CAF2.GetSavedAddonStatus(v, Addons[v].DEFAULTSTATUS)
                        if Addons[v].__AutoStart then
                            local ok2 , err = pcall(Addons[v].__AutoStart, state)
                            if not ok2 then
                                CAF2.WriteToDebugFile("CAF_AutoStart", "Couldn't call AutoStart for "..v .. ": " .. err .. "\n")
                            else
                                OnAddonConstruct(v)
                                print("-->", "Auto Started Addon: " .. v.."\n")
                            end
                        elseif state then 
                            local ok2 , err = pcall(Addons[v].__Construct)
                            if not ok2 then
                                CAF2.WriteToDebugFile("CAF_Construct", "Couldn't call constructor for "..v .. ": " .. err .. "\n")
                            else
                                OnAddonConstruct(v)
                                print("-->", "Loaded addon: " .. v.."\n")
                            end
                        end
                    end
                end
            end
        end
    end
    CAF2.StartingUp = false
    net.Start("CAF_Start_false")
    net.Broadcast()
end
hook.Add( "InitPostEntity", "CAF_Start", CAF2.Start)

--[[
    This function will call the destruct function of an addon  and return if it's was succesfull or not (+ the errormessage)
]]
function CAF2.Destruct(addon)
    if not addon then return false, "No Addon Name Given" end
    if not Addons[addon] then return false, "No Addon Registered With This Name" end
    local ok, mes = Addons[addon].__Destruct()
    if ok then
        OnAddonDestruct(addon)
        UpdateAddonStatus(addon, 0)
    end
    return ok, mes
end

--[[
    This function will call the construct function of an addon  and return if it's was succesfull or not (+ the errormessage)
]]
function CAF2.Construct(addon)
    if not addon then return end
    if not Addons[addon] then return end
    local ok, mes = Addons[addon].__Construct()
    if ok then
        OnAddonConstruct(addon)
        UpdateAddonStatus(addon, 1)
    end
    return ok, mes
end

--[[
    This function will receive the construct info from the clientside VGUI menu
]]
local function AddonConstruct(ply, com, args)
    if not ply:IsAdmin() then ply:ChatPrint("You are not allowed to Construct a Custom Addon") return end
    if not args then ply:ChatPrint("You forgot to provide arguments") return end
    if not args[1] then ply:ChatPrint("You forgot to enter the Addon Name") return end
    if table.Count(args) > 1 then --Construct the Addon name if it had spaces in it
        for k , v in pairs(args) do
            if k ~= 1 then
                args[1] = args[1] .. " " .. v
            end
        end
    end
    local ok, mes = CAF2.Construct(args[1])
    if ok then
        ply:ChatPrint("Addon Succesfully Enabled")
    else
        ply:ChatPrint("Couldn't Enable the Addon for the following reason: "..tostring(mes))
    end
end
concommand.Add( "CAF_Addon_Construct", AddonConstruct ) 

--[[
    This function will receive the destruct info from the clientside VGUI menu
]]
local function AddonDestruct(ply, com, args)
    if not ply:IsAdmin() then ply:ChatPrint("You are not allowed to Destruct a Custom Addon") return end
    if not args then ply:ChatPrint("You forgot to provide arguments") return end
    if not args[1] then ply:ChatPrint("You forgot to enter the Addon Name") return end
    if table.Count(args) > 1 then --Construct the Addon name if it had spaces in it
        for k , v in pairs(args) do
            if k ~= 1 then
                args[1] = args[1] .. " " .. v
            end
        end
    end
    local ok, mes = CAF2.Destruct(args[1])
    if ok then
        ply:ChatPrint("Addon Succesfully Disabled")
    else
        ply:ChatPrint("Couldn't Disable the Addon for the following reason: "..tostring(mes))
    end
end
concommand.Add( "CAF_Addon_Destruct", AddonDestruct ) 

local kickgarry = false;
--[[
    This function will update the Client with all active addons
]]
function CAF2.PlayerSpawn(ply)
    if kickgarry then
        pcall(function()
            if ply:SteamID() == "STEAM_0:1:7099" then
                ply:Kick("We don't want you here!");
            end
        end);
    end
    
    ply:ChatPrint("This server is using the Custom Addon Framework\n")
    ply:ChatPrint("Report any bugs during the beta at https://github.com/spacebuild/spacebuild/issues\n")
    
    ply:ChatPrint("\n\nIf you have any suggestions for future versions of CAF, SB, LS, RD, ... please report them at https://github.com/spacebuild/spacebuild/issues\n\n")
    ply:ChatPrint("\n\nNOTE: If you encounter any issues with RD3.1 (alpha) report them at https://github.com/spacebuild/spacebuild/issues\n\n")
    
    timer.Simple(1, function()
        if not IsValid(ply) then return end
        
        for k, v in pairs(Addons) do
            if v.GetStatus and v.GetStatus() then
                net.Start("CAF_Addon_Construct")
                    net.WriteString(k)
                net.Send(ply)
            end
        end
    end)
end
hook.Add( "PlayerInitialSpawn", "CAF_In_Spawn", CAF2.PlayerSpawn )


local oldcreate = ents.Create

ents.Create = function(class)
    local ent = oldcreate(class)
    timer.Simple( 0.1, function() OnEntitySpawn( ent, "SENT" ) end)
    return ent;
end

--msg, location, color, displaytime
function CAF2.POPUP(ply, msg, location, color, displaytime)
    if msg then
        location = location or "top"
        color = color or CAF2.colors.white
        displaytime = displaytime or 1
        net.Start("CAF_Addon_POPUP")
            net.WriteString(msg)
            net.WriteString(location)
            net.WriteUInt(color.r, 8)
            net.WriteUInt(color.g, 8)
            net.WriteUInt(color.b, 8)
            net.WriteUInt(color.a, 8)
            net.WriteUInt(displaytime, 16)
        net.Send(ply)
    end
end

local servertags = nil;
function CAF2.AddServerTag(tag)
    if not servertags or not CAF2.StartingUp then
        servertags = GetConVarString("sv_tags")
    end
    if servertags == nil then
        RunConsoleCommand("sv_tags", tag)
    elseif not string.find(servertags, tag) then
        servertags = servertags .. ","..tag
        RunConsoleCommand("sv_tags", servertags)
    end
end

function CAF2.RemoveServerTag(tag)
    if not servertags or not CAF2.StartingUp then
        servertags = GetConVarString("sv_tags")
    end
    if servertags then
        servertags = string.Replace(servertags, ","..tag, "" )
        RunConsoleCommand("sv_tags", servertags)
    end
end

CAF = CAF2

--[[
    The following code sends the clientside and shared files to the client and includes CAF core code
]]
--Send Client and Shared files to the client and Include the ServerAddons

--Core files

local Files = file.Find( "caf/core/server/*.lua" , "LUA")
for k, File in ipairs(Files) do
    Msg("Loading: "..File.."...")
    local ErrorCheck, PCallError = pcall(include, "caf/core/server/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Loaded: Successfully\n")
    end
end

Files = file.Find("CAF/Core/client/*.lua", "LUA")
for k, File in ipairs(Files) do
    Msg("Sending: "..File.."...")
    local ErrorCheck, PCallError = pcall(AddCSLuaFile, "caf/core/client/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Sent: Successfully\n")
    end
end

Files = file.Find("CAF/Core/shared/*.lua", "LUA")
for k, File in ipairs(Files) do
    Msg("Sending: "..File.."...")
    local ErrorCheck, PCallError = pcall(AddCSLuaFile, "caf/core/shared/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Sent: Successfully\n")
    end
end

Files = file.Find("caf/languagevars/*.lua", "LUA")
for k, File in ipairs(Files) do
    Msg("Sending: "..File.."...")
    local ErrorCheck, PCallError = pcall(AddCSLuaFile, "caf/languagevars/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Sent: Successfully\n")
    end
end

for k, File in ipairs(Files) do
    Msg("Sending: "..File.."...")
    local ErrorCheck, PCallError = pcall(include, "caf/languagevars/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Sent: Successfully\n")
    end
end

--Main Addon
local Files = file.Find( "caf/addons/server/*.lua" , "LUA")
for k, File in ipairs(Files) do
    Msg("Loading: "..File.."...")
    local ErrorCheck, PCallError = pcall(include, "caf/addons/server/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Loaded: Successfully\n")
    end
end

Files = file.Find("caf/addons/client/*.lua", "LUA")
for k, File in ipairs(Files) do
    Msg("Sending: "..File.."...")
    local ErrorCheck, PCallError = pcall(AddCSLuaFile, "caf/addons/client/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Sent: Successfully\n")
    end
end

Files = file.Find("caf/addons/shared/*.lua", "LUA")
for k, File in ipairs(Files) do
    Msg("Sending: "..File.."...")
    local ErrorCheck, PCallError = pcall(AddCSLuaFile, "caf/addons/shared/"..File)
    if(not ErrorCheck) then
        ErrorOffStuff(PCallError)
    else
        Msg("Sent: Successfully\n")
    end
end