428 lines
12 KiB
Lua
428 lines
12 KiB
Lua
if string.match(debug.getinfo(1).source, '=%[SMODS %w+ ".+"]') then
|
|
error("Please update your steamodded thanks")
|
|
end
|
|
|
|
local util = require("debugplus.util")
|
|
local loggerSucc, logger = pcall(require, "debugplus.logger")
|
|
local global = {}
|
|
|
|
if not loggerSucc then -- To handle older lovely versions, where I can't properly load my deps.
|
|
return {
|
|
getValue = function() -- Can't error myself because it's not propagated, so I error in the first function that is called.
|
|
error("DebugPlus couldn't load a required component. Please make sure your lovely is up to date.\nYou can grab the latest lovely at: https://github.com/ethangreen-dev/lovely-injector/releases\n\n".. (logger or "No further info"))
|
|
end
|
|
}
|
|
end
|
|
|
|
|
|
local configDefinition = {
|
|
debugMode = {
|
|
label = "Debug Mode",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {"Toggles everything in DebugPlus except the console."},
|
|
onUpdate = function(v) _RELEASE_MODE = not v end
|
|
},
|
|
ctrlKeybinds = {
|
|
label = util.ctrlText .. " for Keybinds",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {"Requires you hold ".. util.ctrlText .. " when pressing the built in keybinds."}
|
|
},
|
|
logLevel = {
|
|
label = "Log Level",
|
|
type = "select",
|
|
default = "INFO",
|
|
values = {"ERROR", "WARN", "INFO", "DEBUG"},
|
|
info = {
|
|
"Only shows you logs of a certain level. This setting ignore command logs.",
|
|
"Will show all logs for the selected level and higher."
|
|
},
|
|
-- Most of the time I wouldn't define onUpdate here for something in another module,
|
|
-- but I need to avoid circular dependencies and want the logger here.
|
|
onUpdate = function(v)
|
|
for k, v in pairs(logger.levelMeta) do
|
|
v.shouldShow = false
|
|
end
|
|
|
|
logger.levelMeta.ERROR.shouldShow = true
|
|
if v == "ERROR" then return logger.handleLogsChange() end
|
|
logger.levelMeta.WARN.shouldShow = true
|
|
if v == "WARN" then return logger.handleLogsChange() end
|
|
logger.levelMeta.INFO.shouldShow = true
|
|
if v == "INFO" then return logger.handleLogsChange() end
|
|
logger.levelMeta.DEBUG.shouldShow = true
|
|
logger.handleLogsChange()
|
|
end
|
|
},
|
|
showNewLogs = {
|
|
label = "Show New Logs",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {
|
|
"Show a message when something is logged. Can also press shift + / to temporarily toggle."
|
|
}
|
|
},
|
|
onlyCommands = {
|
|
label = "Only Show Commands",
|
|
type = "toggle",
|
|
default = false,
|
|
info = {"Do not show any logs, other than ones from commands or from you pressing debug keybinds."}
|
|
},
|
|
showHUD = {
|
|
label = "Show Debug HUD",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {"Shows some debug information on the top left of the screen."}
|
|
},
|
|
-- Hidden config for rn. Even though they are hidden at the default logging level,
|
|
-- they are so frequent is starts clearing normal logs if left run for a bit.
|
|
-- This option was added so if someone does want them, they can be reenabled.
|
|
enableLongDT = {
|
|
label = "Enable Long DT Messages",
|
|
type = "toggle",
|
|
default = false,
|
|
info = {}
|
|
},
|
|
processTables = {
|
|
label = "Automatically Expand Printed Tables",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {"When a table is printed, expand it's contents (like in the eval command) instead of just strigifying it."}
|
|
},
|
|
stringifyPrint = {
|
|
label = "Process Arguments Before Logging",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {
|
|
"When this is enabled and something is printed to the lovely console/log DebugPlus will handle the processing of the args before logging them.",
|
|
"This allows the 'Automatically Expand Printed Tables' option to also show up in those logs."
|
|
}
|
|
},
|
|
hyjackErrorHandler = {
|
|
label = "Console In Crash Handler",
|
|
type = "toggle",
|
|
default = true,
|
|
info = {
|
|
"When this is toggled, DebugPlus's console will be accessible in the error handler.",
|
|
"Requires Steamodded (or another tool to replace the error handler) to function",
|
|
"Requires a restart for the toggle to take effect"
|
|
}
|
|
}
|
|
}
|
|
|
|
global.configDefinition = configDefinition
|
|
|
|
local configPages = { -- TODO: implement paging, maybe only when I need to
|
|
{
|
|
name = "Console",
|
|
"showNewLogs",
|
|
"onlyCommands",
|
|
"logLevel",
|
|
"processTables",
|
|
"stringifyPrint",
|
|
"hyjackErrorHandler",
|
|
},
|
|
{
|
|
name = "Misc",
|
|
"debugMode",
|
|
"ctrlKeybinds",
|
|
"showHUD",
|
|
}
|
|
}
|
|
|
|
for k,v in pairs(configDefinition) do
|
|
v.key = k
|
|
end
|
|
|
|
local testValues = {}
|
|
local configTypes
|
|
local configMemory
|
|
|
|
local function parseConfigValue(val)
|
|
val = util.trim(val)
|
|
if val == "true" then
|
|
return true
|
|
end
|
|
if val == "false" then
|
|
return false
|
|
end
|
|
if val:sub(1, 1) == '"' and val:sub(#val) == '"' then
|
|
return val:sub(2, #val - 1):gsub("\\(.?)", {
|
|
["\\"] = "\\",
|
|
n = "\n",
|
|
r = "\r"
|
|
})
|
|
end
|
|
if tonumber(val) then
|
|
return tonumber(val)
|
|
end
|
|
return {
|
|
type = "raw",
|
|
val = val
|
|
}
|
|
end
|
|
|
|
local function stringifyConfigValue(val)
|
|
if val == true then
|
|
return "true"
|
|
end
|
|
if val == false then
|
|
return "false"
|
|
end
|
|
if type(val) == "string" then
|
|
return '"' .. val:gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub("\r", "\\r") .. '"'
|
|
end
|
|
if type(val) == "number" then
|
|
return string.format("%g", val)
|
|
end
|
|
if val.type == "raw" then
|
|
return val.val
|
|
end
|
|
end
|
|
|
|
local function parseConfigFile(data)
|
|
local t = {}
|
|
for str in string.gmatch(data, "([^\n\r]+)") do
|
|
local name, val = str:match("(%w+)%s*=%s*(.+)")
|
|
if not name then
|
|
logger.errorLog("Failed to parse line:", str)
|
|
else
|
|
t[name] = parseConfigValue(val)
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
local function stringifyConfigFile(data)
|
|
local str = ""
|
|
for k, v in pairs(data) do
|
|
local val = stringifyConfigValue(v)
|
|
if val then
|
|
str = str .. k .. "=" .. val .. "\n"
|
|
end
|
|
end
|
|
return str
|
|
end
|
|
|
|
local function loadSaveFromFile()
|
|
local content = love.filesystem.read("config/DebugPlus.jkr")
|
|
if not content then
|
|
return {}
|
|
end
|
|
local success, res = pcall(parseConfigFile, content)
|
|
if success and type(res) == "table" then
|
|
return res
|
|
end
|
|
logger.errorLog("Loading save err", res)
|
|
return {}
|
|
end
|
|
|
|
|
|
local function generateSaveFileTable()
|
|
if not configMemory then return loadSaveFromFile() end
|
|
local fin = {}
|
|
for k, v in pairs(configMemory) do
|
|
fin[k] = v.store
|
|
end
|
|
return fin
|
|
end
|
|
|
|
local function updateSaveFile()
|
|
local conf = generateSaveFileTable()
|
|
love.filesystem.createDirectory("config")
|
|
local success, res = pcall(stringifyConfigFile, conf)
|
|
if success then
|
|
love.filesystem.write("config/DebugPlus.jkr", res)
|
|
else
|
|
logger.errorLog("Failure saving config", res)
|
|
end
|
|
end
|
|
|
|
|
|
function global.setValue(key, value)
|
|
local def = configDefinition[key]
|
|
if not def then return end
|
|
if configTypes[def.type] and configTypes[def.type].validate then
|
|
if not configTypes[def.type].validate(value, def) then
|
|
logger.errorLog('Value for saving key ' .. key .. ' failed to validate')
|
|
return
|
|
end
|
|
end
|
|
local mem = configMemory[key]
|
|
mem.store = value
|
|
mem.value = value
|
|
if def.onUpdate then
|
|
def.onUpdate(value)
|
|
end
|
|
updateSaveFile()
|
|
end
|
|
|
|
function global.clearValue(key)
|
|
local def = configDefinition[key]
|
|
if not def then return end
|
|
local mem = configMemory[key]
|
|
mem.store = nil
|
|
mem.value = def.default
|
|
if def.onUpdate then
|
|
def.onUpdate(value)
|
|
end
|
|
updateSaveFile()
|
|
end
|
|
|
|
function global.getValue(key)
|
|
local def = configDefinition[key]
|
|
if not def then return end
|
|
return configMemory[key].value
|
|
end
|
|
|
|
configTypes = {
|
|
toggle = {
|
|
validate = function(data, def)
|
|
return type(data) == "boolean"
|
|
end,
|
|
render = function(def)
|
|
return create_toggle({
|
|
label = def.label,
|
|
ref_table = configMemory[def.key],
|
|
ref_value = "value",
|
|
callback = function(v) global.setValue(def.key, v) end,
|
|
info = def.info
|
|
})
|
|
end
|
|
},
|
|
select = {
|
|
validate = function(data, def)
|
|
return util.hasValue(def.values, data)
|
|
end,
|
|
render = function(def)
|
|
local curr = util.hasValue(def.values, configMemory[def.key].value) or 1
|
|
return create_option_cycle({
|
|
options = def.values,
|
|
current_option = curr,
|
|
scale = 0.8,
|
|
opt_callback = "DP_conf_select_callback",
|
|
label = def.label,
|
|
info = def.info,
|
|
dp_key = def.key
|
|
})
|
|
end
|
|
},
|
|
}
|
|
|
|
local function getDefaultsObject()
|
|
local config = {}
|
|
for k, v in pairs(configDefinition) do
|
|
config[k] = v.default
|
|
end
|
|
return config
|
|
end
|
|
|
|
|
|
local function generateMemory()
|
|
local defaults = getDefaultsObject()
|
|
local loaded = loadSaveFromFile()
|
|
|
|
configMemory = {}
|
|
|
|
for k, v in pairs(loaded) do
|
|
local store = v
|
|
local value = nil
|
|
local def = configDefinition[k]
|
|
if def then
|
|
if configTypes[def.type] and configTypes[def.type].validate then
|
|
if configTypes[def.type].validate(v, def) then
|
|
value = v
|
|
else
|
|
logger.errorLog('Value for saved key ' .. k .. ' failed to validate')
|
|
value = def.default
|
|
end
|
|
else
|
|
value = v
|
|
end
|
|
if def.onUpdate then
|
|
def.onUpdate(value)
|
|
end
|
|
end
|
|
configMemory[k] = {
|
|
store = store,
|
|
value = value,
|
|
}
|
|
end
|
|
|
|
for k, v in pairs(defaults) do
|
|
if configMemory[k] then
|
|
goto continue
|
|
end
|
|
configMemory[k] = {
|
|
store = nil,
|
|
value = v,
|
|
}
|
|
::continue::
|
|
end
|
|
end
|
|
|
|
function global.generateConfigTab(arg)
|
|
local index = arg.index or 1
|
|
function G.FUNCS.DP_conf_select_callback(e)
|
|
global.setValue(e.cycle_config.dp_key, e.to_val)
|
|
end
|
|
local nodes = {}
|
|
for k,v in ipairs(configPages[index]) do
|
|
local def = configDefinition[v]
|
|
table.insert(nodes, configTypes[def.type].render(def))
|
|
end
|
|
return {
|
|
-- ROOT NODE
|
|
n = G.UIT.ROOT,
|
|
config = {r = 0.1, minw = 7, minh = 5, align = "cm", padding = arg.source == "lovely" and .05 or .5, colour = arg.source == "lovely" and G.C.CLEAR or G.C.BLACK},
|
|
nodes = {
|
|
{
|
|
-- COLUMN NODE TO ALIGN EVERYTHING INSIDE VERTICALLY
|
|
n = G.UIT.C,
|
|
config = {align = "tm", padding = 0.1},
|
|
nodes = nodes
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
function global.generateConfigTabs(source)
|
|
local tab = {}
|
|
for i,v in ipairs(configPages) do
|
|
table.insert(tab, {
|
|
label = v.name,
|
|
tab_definition_function = global.generateConfigTab,
|
|
tab_definition_function_args = {source = source, index = i }
|
|
})
|
|
end
|
|
return tab
|
|
end
|
|
|
|
function global.fakeConfigTab()
|
|
local tabs = global.generateConfigTabs("lovely")
|
|
tabs[1].chosen = true
|
|
G.FUNCS.overlay_menu({
|
|
definition = create_UIBox_generic_options({
|
|
back_func = "settings",
|
|
contents = {create_tabs({
|
|
snap_to_nav = true,
|
|
-- colour = {.65, .36, 1, 1},
|
|
tabs = tabs,
|
|
tab_h = 7.05,
|
|
tab_alignment = 'tm',
|
|
})}
|
|
})
|
|
})
|
|
return {}
|
|
end
|
|
|
|
generateMemory()
|
|
|
|
-- if debug.getinfo(1).source:match("@.*") then -- For when running under watch
|
|
-- logger.log("DebugPlus config in watch")
|
|
-- return global.generateConfigTab() -- For watch config_tab
|
|
-- end
|
|
|
|
return global
|