-- MonetLoader for Android 3.0.
0
-- Reference script: Script Manager
--
-- For script developers:
-- If you want to allow your script to be toggled from Script Manager, you must
implement the following API in EXPORTS:
-- 1. For "Enabled" checkbox:
-- a. canToggle: return true
-- b. getToggle: return <your toggled status variable>
-- c. toggle: execute any code, and switch <your toggled status variable>
(switching is optional)
-- 2. For "Activate button":
-- a. canToggle: return false
-- b. getToggle: return false
-- c. toggle: execute any code
--
-- Simple example that implements "Enabled" checkbox:
-- local toggled = false
-- EXPORTS = {
-- canToggle = function() return true end,
-- getToggle = function() return toggled end,
-- toggle = function() toggled = not toggled end
-- }
--
-- script info
script_name('Script Manager')
script_version('1.2')
script_version_number(3)
script_author('The MonetLoader Team')
script_description('Script manager that opens on left swipe on radar and provides
ability to manage scripts, view logs, execute Lua code in REPL-like mode and
receive script notifications.')
script_properties('work-in-pause', 'forced-reloading-only') -- work even in pause
and don't reload ourselves on reloadScripts()
-- libs
local levels = require('moonloader').message_prefix
local ffi = require('ffi')
local widgets = require('widgets') -- for WIDGET_(...)
local imgui = require('mimgui')
local faicons = require('fAwesome6')
local cfg = require('jsoncfg')
-- pretty printing ([Link]
table-to-console)
function prettyPrintTable(node)
local cache, stack, output = {},{},{}
local depth = 1
local output_str = "{"
while true do
local size = 0
for k,v in pairs(node) do
size = size + 1
end
local cur_index = 1
for k,v in pairs(node) do
if (cache[node] == nil) or (cur_index >= cache[node]) then
if ([Link](output_str,"}",output_str:len())) then
output_str = output_str .. ","
end
-- This is necessary for working with HUGE tables otherwise we run out of
memory using concat on huge strings
[Link](output,output_str)
output_str = ""
local key
if (type(k) == "string") then
key = "['"..tostring(k).."']"
else
key = "["..tostring(k).."]"
end
if (type(v) ~= "table" and type(v) ~= "string") then
output_str = output_str .. key .. " = "..tostring(v)
elseif (type(v) == "table") then
output_str = output_str .. key .. " = {"
[Link](stack,node)
[Link](stack,v)
cache[node] = cur_index+1
break
else
output_str = output_str .. key .. " = '"..tostring(v).."'"
end
if (cur_index == size) then
output_str = output_str .. "}"
else
output_str = output_str .. ","
end
else
-- close the table
if (cur_index == size) then
output_str = output_str .. "}"
end
end
cur_index = cur_index + 1
end
if (size == 0) then
output_str = output_str .. "}"
end
if (#stack > 0) then
node = stack[#stack]
stack[#stack] = nil
depth = cache[node] == nil and depth + 1 or depth - 1
else
break
end
end
-- This is necessary for working with HUGE tables otherwise we run out of memory
using concat on huge strings
[Link](output,output_str)
output_str = [Link](output)
return output_str
end
-- pretty prints arguments, expanding tables (also supports multiple nils without
omitting them)
function prettyPrint(...)
-- we use select instead of table unpacking in order to handle nil values
correctly
local argc = select('#', ...)
if argc == 0 then
return 'nil'
end
local output_str = ''
for i=1, argc do
local v = select(i, ...)
if type(v) == 'table' then
output_str = output_str .. prettyPrintTable(v)
elseif type(v) == 'string' then
output_str = output_str .. "'" .. v .. "'"
else
output_str = output_str .. tostring(v)
end
if i ~= argc then
output_str = output_str .. ','
end
end
return output_str
end
-- simple ipairs implementation that supports any type
function stateless_iter(a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end
function any_ipairs(a)
return stateless_iter, a, 0
end
-- circular buffer class ([Link]
local function rotate_indice(i, n)
return ((i - 1) % n) + 1
end
local circular_buffer = {}
function circular_buffer.reverse_iter(a, i)
i = i - 1
local v = a[i]
if v then
return i, v
end
end
function circular_buffer.reverse_ipairs(self)
return circular_buffer.reverse_iter, self, 0
end
function circular_buffer.filled(self)
return #([Link]) == self.max_length
end
function circular_buffer.push(self, value)
if self:filled() then
local value_to_be_removed = [Link][[Link]]
[Link][[Link]] = value
[Link] = [Link] == self.max_length and 1 or [Link] + 1
else
[Link][#([Link]) + 1] = value
end
end
function circular_buffer.clear(self)
[Link] = {}
[Link] = 1
end
circular_buffer.metatable = {}
-- positive values index from newest to oldest (starting with 1)
-- negative values index from oldest to newest (starting with -1)
function circular_buffer.metatable.__index(self, i)
local history_length = #([Link])
if i == 0 or [Link](i) > history_length then
return nil
elseif i > 0 then
local i_rotated = rotate_indice([Link] - 1 + i, history_length)
return [Link][i_rotated]
else
local i_rotated = rotate_indice([Link] + i, history_length)
return [Link][i_rotated]
end
end
function circular_buffer.metatable.__len(self)
return #([Link])
end
function circular_buffer.new(max_length)
if type(max_length) ~= 'number' or max_length <= 1 then
error("Buffer length must be a positive integer")
end
local instance = {
history = {},
oldest = 1,
max_length = max_length,
push = circular_buffer.push,
filled = circular_buffer.filled,
clear = circular_buffer.clear
}
setmetatable(instance, circular_buffer.metatable)
return instance
end
-- notifications ([Link]
Notifications = {
_version = '0.2',
_list = {},
_COLORS = {
[0] = {back = {0.26, 0.71, 0.81, 1}, text = {1, 1, 1, 1}, icon = {1, 1, 1,
1}, border = {1, 0, 0, 0}},
[1] = {back = {0.26, 0.81, 0.31, 1}, text = {1, 1, 1, 1}, icon = {1, 1, 1,
1}, border = {1, 0, 0, 0}},
[2] = {back = {1, 0.39, 0.39, 1}, text = {1, 1, 1, 1}, icon = {1, 1, 1,
1}, border = {1, 0, 0, 0}},
[3] = {back = {0.97, 0.57, 0.28, 1}, text = {1, 1, 1, 1}, icon = {1, 1, 1,
1}, border = {1, 0, 0, 0}},
[4] = {back = {0, 0, 0, 1}, text = {1, 1, 1, 1}, icon = {1, 1, 1,
1}, border = {1, 0, 0, 0}},
},
TYPE = {
INFO = 0,
OK = 1,
ERROR = 2,
WARN = 3,
DEBUG = 4
},
ICON = {
[0] = faicons('CIRCLE_INFO'),
[1] = faicons('CHECK'),
[2] = faicons('XMARK'),
[3] = faicons('EXCLAMATION'),
[4] = faicons('WRENCH')
}
}
[Link] = function(text, type, time, colors)
[Link](Notifications._list, {
text = text,
type = type or 2,
time = time or 4,
start = [Link](),
alpha = 0,
colors = colors or Notifications._COLORS[type]
})
end
Notifications._TableToImVec = function(tbl)
return imgui.ImVec4(tbl[1], tbl[2], tbl[3], tbl[4])
end
Notifications._BringFloatTo = function(from, to, start_time, duration)
local timer = [Link]() - start_time
if timer >= 0.00 and timer <= duration then
local count = timer / (duration / 100)
return from + (count * (to - from) / 100), true
end
return (timer > duration) and to or from, false
end
[Link](
function() return #Notifications._list > 0 end,
function(self)
[Link] = true
for k, data in ipairs(Notifications._list) do
--==[ UPDATE ALPHA ]==--
if [Link] == nil then Notifications._list[k].alpha = 0 end
if [Link]() - [Link] < 0.5 then
Notifications._list[k].alpha = Notifications._BringFloatTo(0, 1,
[Link], 0.5)
elseif [Link] - 0.5 < [Link]() - [Link] then
Notifications._list[k].alpha = Notifications._BringFloatTo(1, 0, [Link]
+ [Link] - 0.5, 0.5)
end
--==[ REMOVE ]==--
if [Link]() - [Link] > [Link] then
[Link](Notifications._list, k)
end
end
local resX, resY = getScreenResolution()
local sizeX, sizeY = 300 * MONET_DPI_SCALE, 300 * MONET_DPI_SCALE
[Link](imgui.ImVec2(resX * 0.5, resY * 0.5),
[Link], imgui.ImVec2(0.5, 0.5))
[Link](imgui.ImVec2(sizeX, sizeY), [Link])
[Link]('notf_window', _, 0
+ [Link]
+ [Link]
+ [Link]
+ [Link]
+ [Link]
)
local fiveSc = 5 * MONET_DPI_SCALE
local winSize = [Link]()
imgui.SetWindowPosVec2(imgui.ImVec2(resX - 10 * MONET_DPI_SCALE - winSize.x,
resY * 0.4))
for k, data in ipairs(Notifications._list) do
------------------------------------------------
local default_data = {
text = 'text',
type = 0,
time = 1500
}
for k, v in pairs(default_data) do
if data[k] == nil then
data[k] = v
end
end
local c = [Link]()
local p = [Link]()
local DL = [Link]()
local textSize = [Link]([Link])
local iconSize = [Link]([Link][[Link]] or
faicons('XMARK'))
local size = imgui.ImVec2(fiveSc + iconSize.x + fiveSc + textSize.x + fiveSc,
fiveSc + textSize.y + fiveSc)
local winSize = [Link]()
if winSize.x > size.x + 20 * MONET_DPI_SCALE then
[Link](winSize.x - size.x - 8 * MONET_DPI_SCALE)
end
[Link]([Link], [Link])
[Link]([Link], fiveSc)
[Link]([Link],
Notifications._TableToImVec([Link] or
Notifications._COLORS[[Link]].back))
[Link]([Link],
Notifications._TableToImVec([Link] or
Notifications._COLORS[[Link]].border))
[Link]('toastNotf:'..tostring(k)..tostring([Link]), size, true,
[Link] + [Link])
[Link]([Link],
Notifications._TableToImVec([Link] or
Notifications._COLORS[[Link]].icon))
[Link](imgui.ImVec2(fiveSc, size.y / 2 - iconSize.y / 2))
[Link]([Link][[Link]] or faicons('XMARK'))
[Link]()
[Link]([Link],
Notifications._TableToImVec([Link] or
Notifications._COLORS[[Link]].text))
[Link](imgui.ImVec2(fiveSc + iconSize.x + fiveSc, size.y / 2 -
textSize.y / 2))
[Link]([Link])
[Link]()
[Link]()
[Link](2)
[Link](2)
------------------------------------------------
end
[Link]()
end
)
-- global vars
local DEFAULT_CONFIG = { -- default config
crashNotifications = true, -- whether to show script crash notifications or not
scriptMessageNotifications = false, -- whether to show script message
notifications or not
messagesCount = 100, -- count of saved messages
lastCrashesCount = 10, -- count of saved crashed scripts
shellHistoryCount = 50 -- count of saved shell history
}
local config = [Link](DEFAULT_CONFIG) -- simply config
local messages = circular_buffer.new([Link]) -- buffer that stores
last messages
local lastCrashes = circular_buffer.new([Link]) -- buffer that
stores script info about last crashes
local shellHistory = circular_buffer.new([Link]) -- buffer that
stores shell history
local shellInputHistory = circular_buffer.new([Link]([Link] /
2)) -- buffer that stores shell input history
local shellInputHistoryPos = 0 -- current position in shellInputHistory
local scriptCrashInfos = {} -- buffer that stores reasons for script crash
local reloadLastCrashInfos = {} -- buffer that stores crash info that initiated
reload for a given path
local selectedScriptId = -1 -- id of selected script
local selectedScriptExports -- table returned by import on selected script
local wasInLog = false -- set to true when tab is log, used to auto-scroll to
bottom on tab switch
local wasInShell = false -- same, but with shell
local windowState = [Link](false) -- script mgr window is active or not
-- some imgui wrappers
local imScriptStatus = [Link](false) -- ffi variable for script toggling
local imCrashNotifications = [Link]([Link])
local imScriptMessageNotifications =
[Link]([Link])
local imMessagesCount = [Link]([Link])
local imLastCrashesCount = [Link]([Link])
local imShellHistoryCount = [Link]([Link])
local scriptsSearchBuffer = [Link][128]() -- buffer for scripts search
input
local scriptsSearchText = '' -- scripts search input as lua string
local logSearchBuffer = [Link][128]() -- buffer for log search input
local logSearchText = '' -- log search input as lua string
local shellInputBuffer = [Link][512]() -- buffer for shell input
-- utils
-- formats time in seconds into format: xxh xxm xxs (hours and minutes are omitted
if not present)
function formatClock(diff)
diff = [Link](diff)
local seconds = diff % 60
diff = [Link](diff / 60)
local minutes = diff % 60
diff = [Link](diff / 60)
local hours = diff
return (hours > 0 and tostring(hours) .. 'h ' or '') .. (minutes > 0 and
tostring(minutes) .. 'm ' or '') .. tostring(seconds) .. 's'
end
-- [Link]
[Link](function()
[Link]().IniFilename = nil
local config = [Link]()
[Link] = true
[Link] = true
-- bake only needed glyphs in atlas in order to not waste videomemory
local builder = [Link]()
for _, v in pairs([Link]) do
builder:AddText(v)
end
glyphRanges = imgui.ImVector_ImWchar() -- global, because it must be present
until font atlas is built
builder:BuildRanges(glyphRanges)
[Link]().Fonts:AddFontFromMemoryCompressedBase85TTF(faicons.get_font_data_base
85('solid'), 14 * MONET_DPI_SCALE, config, glyphRanges[0].Data) -- load scaled DPI
font
[Link]():ScaleAllSizes(MONET_DPI_SCALE) -- scale default style
end)
-- rendering
-- main window
[Link](
function() return windowState[0] end,
function(self)
[Link](imgui.ImVec2(530 * MONET_DPI_SCALE, 330 *
MONET_DPI_SCALE), [Link])
[Link]('Script Manager para MonetLoader v' .. [Link],
windowState, [Link])
if [Link]('Tabs') then
local didLogRender = false
local didShellRender = false
if [Link]('Scripts') then -- common scripts control
if [Link]('##ScriptsSearch', 'Procurar....',
scriptsSearchBuffer, [Link](scriptsSearchBuffer)) then
scriptsSearchText = [Link](scriptsSearchBuffer):lower()
end
[Link]()
if [Link]('Recarregar tudo') then
[Link]('Confirme o recarregamento de tudol')
end
if [Link]('Confirme o recarregamento de tudol') then
[Link]('Tem certeza de que deseja recarregar todos os scripts?')
if [Link]('Sim', imgui.ImVec2(150 * MONET_DPI_SCALE, 50 *
MONET_DPI_SCALE)) then
reloadScripts()
[Link]('Todos os scripts foram recarregados',
[Link])
[Link]()
end
[Link]()
if [Link]('Não', imgui.ImVec2(150 * MONET_DPI_SCALE, 50 *
MONET_DPI_SCALE)) then
[Link]()
end
[Link]()
end
[Link]('##ScriptsChild') -- child in order to only scroll scripts
imgui.PushStyleVarVec2([Link], imgui.ImVec2(0, 0))
[Link](2, '##ScriptsColumns', false)
[Link]()
local scripts = [Link]()
if imgui.ListBoxHeaderVec2('##ScriptsListBox', imgui.ImVec2(-1, -1)) then
for i, v in ipairs(scripts) do
if [Link]:lower():find(scriptsSearchText, 1, true) then
if [Link]([Link] .. '##' .. tostring([Link]),
selectedScriptId == [Link]) then
selectedScriptId = [Link]
selectedScriptExports = [Link]
end
end
end
[Link]()
end
[Link]()
local scr = [Link](selectedScriptId)
if scr ~= nil then
[Link]('Nome: %s', [Link])
if [Link] ~= [Link] then
[Link]('Nome do arquivo: %s', [Link])
end
local version = [Link]
local version_num = scr.version_num
if #version ~= 0 and version_num ~= 0 then
[Link]('Versão: %s (%g)', version, version_num)
elseif #version == 0 and version_num ~= 0 then
[Link]('Versão: %g', version_num)
elseif #version ~= 0 and version_num == 0 then
[Link]('Versão: %s', version)
end
local authors = [Link]([Link], ', ')
if #authors ~= 0 then
[Link]('Autores: %s', authors)
end
local desc = [Link]
if #desc ~= 0 then
[Link]('Descrição: %s', desc)
end
local url = [Link]
if #url ~= 0 then
[Link]('URL: %s', url)
end
if [Link]('Descarregar') then
scr:unload()
[Link]([Link] .. ':\nDescarregado!',
[Link])
end
[Link]()
if [Link]('Recarregar') then
scr:reload()
[Link]([Link] .. ':\nRecarregado!',
[Link])
end
-- pcall hell in order to not crash Script Manager if selected script
implements invalid API
if [Link] ~= nil and
[Link] ~= nil and [Link] ~= nil then
local status, result = pcall([Link])
if not status or type(result) ~= 'boolean' then
[Link]([Link] .. ':\nError calling canToggle!\nMake
sure it returns a boolean.', [Link])
else
if result then
local status2, toggle = pcall([Link])
if not status2 or type(toggle) ~= 'boolean' then
[Link]([Link] .. ':\nError calling getToggle!\nMake
sure it returns a boolean.', [Link])
else
imScriptStatus[0] = toggle
if [Link]('Habilitado', imScriptStatus) then
local status3 = pcall([Link])
if not status3 then
[Link]([Link] .. ':\nError calling toggle!',
[Link])
end
end
end
else
if [Link]('Activate') then
local status2 = pcall([Link])
if not status2 then
[Link]([Link] .. ':\nError calling toggle!',
[Link])
end
end
end
end
end
else
[Link]('<<<\nSelecione qualquer script à esquerda!')
end
[Link](1)
[Link]()
[Link]()
end
if [Link]('Log') then -- log of recent events
if [Link]('##LogSearch', 'Procurar...', logSearchBuffer,
[Link](logSearchBuffer)) then
logSearchText = [Link](logSearchBuffer):lower()
end
[Link]()
if [Link]('Limpar Histórico') then
messages:clear()
end
[Link]('##LogChild') -- child in order to only scroll text
without scrolling search and etc
for i, v in any_ipairs(messages) do
if v:lower():find(logSearchText, 1, true) then
[Link]('%s', v)
end
end
if [Link]() >= [Link]() or not wasInLog then
[Link](1.0)
end
[Link]()
[Link]()
didLogRender = true
end
if [Link]('Últimas falhas') then -- log of last crashes
[Link]('##LastCrashesChild', imgui.ImVec2(0, 0), true) -- child
in order to only scroll table and for border
[Link](3, '##LastCrashesColumns', true)
[Link]()
[Link]('Nome do Script')
[Link]()
[Link]()
[Link]('Tempo desde o acidente')
[Link]()
[Link]()
[Link]('Ações')
[Link]()
[Link]()
for i, v in circular_buffer.reverse_ipairs(lastCrashes) do
if not [Link] then
[Link]()
[Link]('%s', [Link])
[Link]()
[Link]()
[Link]('%s', formatClock([Link]() - [Link]))
[Link]()
if not [Link] then
if [Link]('Recarregar##' .. tostring(i)) then
reloadLastCrashInfos[[Link]] = v
[Link]([Link])
lua_thread.create(function()
wait(0)
if not [Link] then
[Link]([Link] .. ':\nFalha ao recarregar!',
[Link])
reloadLastCrashInfos[[Link]] = nil
end
end)
end
[Link]()
end
if [Link]('Esconder##' .. tostring(i)) then
[Link] = true
end
[Link]()
[Link]()
end
end
[Link](1)
[Link]()
[Link]()
end
if [Link]('Shell') then -- lua shell
[Link](-1)
if [Link]('##ShellInput', 'Correr....', shellInputBuffer,
[Link](shellInputBuffer), [Link]) then
local text = [Link](shellInputBuffer)
[Link](shellInputBuffer, '')
shellHistory:push('>> ' .. text)
shellInputHistory:push(text)
shellInputHistoryPos = 0
-- first try to load as expression
local chunk, err = load('return prettyPrint(' .. text .. ')')
if not chunk then
-- then as statement
chunk, err = load(text)
end
if not chunk then
-- compilation failed
shellHistory:push('<!> Syntax error: ' .. tostring(err))
else
-- provide repl result
local result, err = pcall(chunk)
if not result then
shellHistory:push('<!> Error: ' .. tostring(err))
else
shellHistory:push(tostring(err))
end
end
end
if [Link]('Acima') then
shellInputHistoryPos = shellInputHistoryPos - 1
if shellInputHistory[shellInputHistoryPos] ~= nil then
[Link](shellInputBuffer,
shellInputHistory[shellInputHistoryPos])
else
shellInputHistoryPos = shellInputHistoryPos + 1
end
end
[Link]()
if [Link]('Abaixo') then
shellInputHistoryPos = shellInputHistoryPos + 1
if shellInputHistoryPos >= 0 then
shellInputHistoryPos = 0
[Link](shellInputBuffer, '')
else
[Link](shellInputBuffer,
shellInputHistory[shellInputHistoryPos])
end
end
[Link]()
if [Link]('Limpar') then
[Link](shellInputBuffer, '')
shellInputHistoryPos = 0
end
[Link]()
if [Link]('Limpar Histórico') then
[Link](shellInputBuffer, '')
shellInputHistoryPos = 0
shellHistory:clear()
shellInputHistory:clear()
end
[Link]('##ShellChild') -- child in order to only scroll text
without scrolling input and etc
for i, v in any_ipairs(shellHistory) do
local doPop = false
if v:sub(1, 3) == '<!>' then
doPop = true
[Link]([Link], imgui.ImVec4(1.0, 0.0, 0.0, 1.0))
end
[Link]('%s', v)
if doPop then
[Link]()
end
end
if [Link]() >= [Link]() or not wasInShell then
[Link](1.0)
end
[Link]()
[Link]()
didShellRender = true
end
if [Link]('Configurações') then -- settings menu
if [Link]('Notificações de falhas', imCrashNotifications) then
[Link] = imCrashNotifications[0]
[Link](config)
end
if [Link]('Notificações de mensagens de script',
imScriptMessageNotifications) then
[Link] = imScriptMessageNotifications[0]
[Link](config)
end
if [Link]('Contagem de mensagens de logs', imMessagesCount, 1, 20)
then
if imMessagesCount[0] < 10 then
imMessagesCount[0] = 10
elseif imMessagesCount[0] > 5000 then
imMessagesCount[0] = 5000
end
[Link] = imMessagesCount[0]
[Link](config)
local newBuffer = circular_buffer.new([Link])
for i, v in any_ipairs(messages) do
newBuffer:push(v)
end
messages = newBuffer
end
if [Link]('Contagem de falhas de logs', imLastCrashesCount, 1, 1)
then
if imLastCrashesCount[0] < 2 then
imLastCrashesCount[0] = 2
elseif imLastCrashesCount[0] > 100 then
imLastCrashesCount[0] = 100
end
[Link] = imLastCrashesCount[0]
[Link](config)
local newBuffer = circular_buffer.new([Link])
for i, v in any_ipairs(lastCrashes) do
newBuffer:push(v)
end
lastCrashes = newBuffer
end
if [Link]('Contagem de histórico do shell', imShellHistoryCount, 1,
20) then
if imShellHistoryCount[0] < 10 then
imShellHistoryCount[0] = 10
elseif imShellHistoryCount[0] > 5000 then
imShellHistoryCount[0] = 5000
end
[Link] = imShellHistoryCount[0]
[Link](config)
local newBuffer = circular_buffer.new([Link])
for i, v in any_ipairs(shellHistory) do
newBuffer:push(v)
end
shellHistory = newBuffer
local newInputBuffer =
circular_buffer.new([Link]([Link] / 2))
for i, v in any_ipairs(shellInputHistory) do
newInputBuffer:push(v)
end
shellInputHistory = newInputBuffer
end
[Link]()
end
[Link]()
wasInLog = didLogRender
wasInShell = didShellRender
end
[Link]()
end
)
-- custom events
-- called whenever a script crashes
function onScriptCrashed(scr, msg)
if [Link] then
[Link]([Link] .. ':\nFalha!', [Link])
end
lastCrashes:push({
name = [Link],
path = [Link],
time = [Link](),
reloaded = false,
hidden = false
})
messages:push('(crash) ' .. [Link] .. ': ' .. msg)
end
-- events
-- script message handler, save them to buffer
function onScriptMessage(msg, scr)
if [Link] then
[Link]([Link] .. ':\n' .. msg, [Link])
end
messages:push('(script) ' .. [Link] .. ': ' .. msg)
end
-- system message handler, get crash info
function onSystemMessage(msg, level, scr)
if level == levels.TYPE_SYSTEM then
if scr ~= nil then
messages:push('(system) ' .. [Link] .. ': ' .. msg)
else
messages:push('(system) ' .. msg)
end
return
end
if scr ~= nil and level == levels.TYPE_ERROR then
if msg:find('Script died due to') and scriptCrashInfos[[Link]] ~= nil then
scriptCrashInfos[[Link]].crashed = true
else
scriptCrashInfos[[Link]] = {
message = msg,
crashed = false
}
end
end
end
-- invoke onScriptCrashed if terminate was called due to script crash
function onScriptTerminate(scr, quit)
if quit then return end
if scriptCrashInfos[[Link]] ~= nil then
if scriptCrashInfos[[Link]].crashed then
onScriptCrashed(scr, scriptCrashInfos[[Link]].message)
end
scriptCrashInfos[[Link]] = nil
end
-- if scr == [Link] then
-- [Link](config)
-- end
end
-- mark script as reloaded in last crashes if it was loaded
function onScriptLoad(scr)
local path = [Link]
if reloadLastCrashInfos[path] ~= nil then
local v = reloadLastCrashInfos[path]
[Link] = true
[Link]([Link] .. ':\nRecarregado!', [Link])
reloadLastCrashInfos[path] = nil
else
for i, v in any_ipairs(lastCrashes) do
if [Link] == path then
[Link] = true
end
end
end
end
-- check for menu opening
function main()
while true do
if isWidgetSwipedLeft(WIDGET_RADAR) then
windowState[0] = not windowState[0]
end
wait(0)
end
end