--@name Logan's Starfall Toolkit --@author logan2611 --@server --@include lsft/cl_lsft.txt --@includedir lsft/modules/server --@includedir lsft/modules/shared --@clientmain lsft/cl_lsft.txt -- SPAWN THIS ONE --[[ _ ____ _____ _____ | | / ___|| ___|_ _| | | _____ \___ \| |_ | | | |___ |_____| ___) | _| | | |_____| |____/|_| |_| --]] --[[ TODO: Update checker TODO: On module load, grab default config values. Write changed values prop.createSent(chip():getPos()+Vector(0,0,90),Angle(0,0,0),"Seat_Airboat",true) --]] -- Main Module (Mostly loading modules and stuff) core = {} do local function stats() net.start("LSFT-StatsRequest") net.send() local peakClientCPUTime = 0 local peakClientCPUPercent = 0 local peakClientRAM = 0 local peakClientRAMPercent = 0 local minClientNet = 2^31 local plys = 0 local function displayStats() core:logConsole(log.TEXT, "---------------------------") core:logConsole(log.TEXT, "Server Stats:") core:logConsole(log.INFO, "Max CPU: ", core.colors.text, math.round(core.maxCPU*1000000).."us ("..math.round(core.maxCPU*100/quotaMax()).."%)") core:logConsole(log.INFO, "Max RAM: ", core.colors.text, math.round(core.maxRAM).."kB ("..math.round(core.maxRAM*100/ramMax()).."%)") core:logConsole(log.INFO, "Min Net: ", core.colors.text, core.minNet.." bytes") core:logConsole(log.TEXT, "") core:logConsole(log.TEXT, "Client Stats:") core:logConsole(log.INFO, "Max CPU: ", core.colors.text, math.round(peakClientCPUTime*1000000).."us") core:logConsole(log.INFO, "Max CPU %: ", core.colors.text, math.round(peakClientCPUPercent*100, 2).."%") core:logConsole(log.INFO, "Max RAM: ", core.colors.text, math.round(peakClientRAM).."kB") core:logConsole(log.INFO, "Max RAM %: ", core.colors.text, math.round(peakClientRAMPercent*100, 2).."%") core:logConsole(log.INFO, "Min Net: ", core.colors.text, minClientNet.." bytes") core:logConsole(log.INFO, "Errored Clients: ", core.colors.text, #chip():getErroredPlayers()) core:logConsole(log.TEXT, "---------------------------") core:log(log.INFO, "Finished, check your developer console.") end timer.create("displaystats", 5, 1, displayStats) net.receive("LSFT-StatsResponse", function() plys = plys + 1 local clientCPUTime = net.readFloat() local clientCPUPercent = clientCPUTime/net.readFloat() local clientRAM = net.readFloat() local clientRAMPercent = clientRAM/net.readFloat() local clientNet = net.readFloat() -- Comparison if clientCPUTime > peakClientCPUTime then peakClientCPUTime = clientCPUTime end if clientCPUPercent > peakClientCPUPercent then peakClientCPUPercent = clientCPUPercent end if clientRAM > peakClientRAM then peakClientRAM = clientRAM end if clientRAMPercent > peakClientRAMPercent then peakClientRAMPercent = clientRAMPercent end if clientNet < minClientNet then minClientNet = clientNet end -- If we have gotten a response from everyone, fuck the timer -- There is a function that tells us when players are ready to receive stuff, but idk where it is and I'm too lazy to make one if plys == #find.allPlayers() then timer.remove("displaystats") displayStats() end end) core:log(log.INFO, "Collecting data, please wait...") end local function hud() if core.hud then enableHud(owner(),false) core.hud = false else enableHud(owner(),true) core.hud = true end end local function help(args) local module = args[1] local command = args[2] if module == nil and command == nil then core:log(log.TEXT, "---------------------------") core:log(log.INFO, "To list the commands in a module, type "..core:get_config("core", "command_prefix").."help ") core:log(log.INFO, "List of available modules:") local modules = {} for i, v in pairs(core.modules) do table.insert(modules, core.colors.logo) table.insert(modules, "[L-SFT] ") table.insert(modules, core.colors.info) table.insert(modules, "\""..i.."\" | ") table.insert(modules, core.colors.text) table.insert(modules, (v.desc or "No description provided.").."\n") end modules[#modules] = modules[#modules]:sub(1, -2) print(unpack(modules)) core:log(log.TEXT, "---------------------------") elseif module ~= nil and command == nil then if not core.modules[module] then core:log(log.ERROR, "Module does not exist!") return end core:log(log.TEXT, "---------------------------") core:log(log.INFO, "List of available commands in module \""..module.."\":") local commands = {} for i, v in pairs(core.modules[module].commands) do table.insert(commands, core.colors.logo) table.insert(commands, "[L-SFT] ") table.insert(commands, core.colors.info) table.insert(commands, "\""..i.."\" | Usage: ") table.insert(commands, core.colors.text) table.insert(commands, (v.usage or "No usage provided.").."\n") end commands[#commands] = commands[#commands]:sub(1, -2) print(unpack(commands)) core:log(log.TEXT, "---------------------------") elseif module ~= nil and command ~= nil then if not core.modules[module].commands[command] then core:log(log.ERROR, "Command does not exist in module \""..module.."\"!") return end core:log(log.TEXT, "---------------------------") core:log(log.INFO, "Usage: ", core.colors.text, (core.modules[module].commands[command].usage or "Command: "..command.." (No usage provided)")) core:log(log.INFO, "Description: ", core.colors.text, (core.modules[module].commands[command].desc or "No description provided.")) core:log(log.TEXT, "---------------------------") else core:log(log.ERROR, "Universe is broken.") end end core.modules = { core = { version = 0.1, desc = "Built in module.", commands = { stats = { func = stats, usage = "stats", desc = "Gets performance information about this Starfall chip.", }, hud = { func = hud, usage = "hud", desc = "Toggles the built in Starfall HUD.", }, help = { desc = "List modules, list commands in a module or gets information about a specific command.", usage = "help [module] [command]", func = help, }, }, }, } end core.config = {} core.defaultconfig = { core = { command_prefix = "!n", colors = { logo = Color(255,200,50), text = Color(255,255,255), info = Color(150,200,255), warn = Color(255,120,50), error = Color(255,50,50), }, controls = { forward = KEY.W, backward = KEY.S, left = KEY.A, right = KEY.D, up = KEY.SPACE, down = KEY.CTRL, run = KEY.SHIFT, }, }, } core.colors = {} core.maxCPU = 0 core.minNet = net.getBytesLeft() core.maxRAM = 0 log = { ERROR=0, WARNING=1, INFO=2, TEXT=3 } function core:init(config) -- If the streamed config exists and has stuff in it, use it instead of the empty one if config != nil and #config > 0 then core.config = config end core:load_modules() local hud = prop.createComponent(chip():getPos(), Angle(0,0,0), "starfall_hud", "models/bull/dynamicbutton.mdl", true) hud:linkComponent(chip()) hud:setSolid(false) hud:setColor(Color(255,255,255,0)) enableHud(owner(), true) core.hud = true prop.setPropClean(true) core.colors.logo = core:get_config("core","colors","logo") core.colors.text = core:get_config("core","colors","text") core.colors.info = core:get_config("core","colors","info") core.colors.warn = core:get_config("core","colors","warn") core.colors.error = core:get_config("core","colors","error") core.command_prefix = core:get_config("core","command_prefix") core:log(log.INFO, "Loaded v"..core.modules.core.version.." successfully") net.start("LSFT-Loaded") net.send() return true end function core:get_config(...) local args = {...} local cur = core.config for arg in pairs(args) do for i,v in pairs(cur) do if i == args[arg] then if type(v) == "table" then --cur = cur[i] cur = v continue else return v end end end end local cur = core.defaultconfig for arg in pairs(args) do for i,v in pairs(cur) do if i == args[arg] then if type(v) == "table" then --cur = cur[i] cur = v continue else return v end end end end error("Config value requested, but no current or default entry exists!") end function core:load_modules() dodir("lsft/modules/server") dodir("lsft/modules/shared") hook.add("PlayerSay", "CommandCheck", function(ply, str) if ply == owner() and string.find(str, "^"..core.command_prefix) then local stringin = string.explode(" ", str) local command = string.sub(stringin[1], #core.command_prefix+1) local args = {} for i, v in pairs(stringin) do if i == 1 then continue end table.insert(args, v) end for i, v in pairs(core.modules) do for commandin, commandthing in pairs(v["commands"]) do if command == commandin then try( function() commandthing.func({unpack(args)}) end, function(err) core:log(log.ERROR, "Error executing command \""..str.."\"!") core:log(log.ERROR, "Reason: "..err["message"]) end ) return "" end end end --print(core.color_logo, "[L-SFT] ", core.color_error, "Error: \""..command.."\" is not a valid command!") core:log(log.ERROR, "\""..command.."\" is not a valid command!") return "" end end) end function core:log(...) local args = {...} local loglevel = args[1] local message = { unpack(args, 2, #args) } local color = Color(0,0,0) if loglevel == log.ERROR then color = core.colors.error elseif loglevel == log.WARNING then color = core.colors.warn elseif loglevel == log.INFO then color = core.colors.info elseif loglevel == log.TEXT then color = core.colors.text end print(core.colors.logo, "[L-SFT] ", color, unpack(message)) end function core:logConsole(...) local args = {...} local loglevel = args[1] local message = { unpack(args, 2, #args) } local color = Color(0,0,0) if loglevel == log.ERROR then color = core.colors.error elseif loglevel == log.WARNING then color = core.colors.warn elseif loglevel == log.INFO then color = core.colors.info elseif loglevel == log.TEXT then color = core.colors.text end printConsole(core.colors.logo, "[L-SFT] ", color, unpack(message)) end -- Get's a player's entity by searching for it via an inputted string function core:get_entity(ply) if ply == nil then return nil elseif type(ply) == "Player" then return ply elseif ply == "^" then return owner() end local targets = find.playersByName(ply) if #targets == 0 or targets == nil then core:log(log.ERROR, "No targets found!") return nil elseif #targets > 1 then local list = "" for i, v in pairs(target) do list = list..v..", " end list:sub(1, -2) core:log(log.ERROR, "Muliple targets found! ("..list..")") return nil end return targets[1] end net.receive("LSFT-Config-Read", function(len, ply) if ply == owner() then -- Colors settings aren't loaded yet, so use dumb printing method with default colors (Don't ever do this >:c) --print(core.defaultconfig.core.color_logo, "[L-SFT] ", core.defaultconfig.core.color_info, "Downloading configuration: 0%") timer.create("ConfigProgress", 0.25, 0, function() print(core.defaultconfig.core.colors.logo, "[L-SFT] ", core.defaultconfig.core.colors.info, "Downloading configuration: ", math.round(net.getStreamProgress() * 100, 2), "%") end) net.readStream(function(data) timer.remove("ConfigProgress") core:init(json.decode(data)) end) else print(core.defaultconfig.core.colors.logo, "[L-SFT] ", core.defaultconfig.core.colors.error, ply:getName().." is trying to give us a config") end end) net.receive("LSFT-Get-Config", function() net.start(net.readString()) local output = {} while true do local temp = net.readTable() if #temp == 0 then break end net.writeTable({core:get_config(unpack(temp))}) -- what the fuck end net.send() end) timer.create("CPUCheck", 0.5, 0, function() if quotaAverage() > core.maxCPU then core.maxCPU = quotaAverage() end if quotaAverage() > quotaMax() * 0.5 then core:log(log.WARNING, "CPU usage is high! ("..math.round(quotaAverage()*100/quotaMax()).."% remaining)") elseif quotaAverage() > quotaMax() * 0.9 then core:log(log.ERROR, "CPU usage is critically high! ("..math.round(quotaAverage()*100/quotaMax()).."% remaining)") timer.pause("CPUCheck") timer.pause("NetCheck") timer.pause("RAMCheck") timer.simple(10, function() timer.unpause("CPUCheck") timer.unpause("NetCheck") timer.unpause("RAMCheck") end) end end) timer.create("NetCheck", 0.5, 0, function() if net.getBytesLeft() < core.minNet then core.minNet = net.getBytesLeft() end if net.getBytesLeft() < 3000 then core:log(log.WARNING, "Network bandwidth is low! ("..net.getBytesLeft().." bytes left)") elseif net.getBytesLeft() < 1000 then core:log(log.ERROR, "Network bandwidth is critically low! ("..net.getBytesLeft().." bytes left)") end end) timer.create("RAMCheck", 0.5, 0, function() if ramUsed() > core.maxRAM then core.maxRAM = ramUsed() end if ramUsed() > ramMax() * 0.5 then core:log(log.WARNING, "RAM usage is high! ("..math.round(ramUsed()*100/ramMax()).."% used)") elseif ramUsed() > ramMax() * 0.9 then core:log(log.ERROR, "RAM usage is critically high! ("..math.round(ramUsed()*100/ramMax()).."% used)") end end)