From 54f75bca86a9b95ac9a20f6199df44adf61a4cc6 Mon Sep 17 00:00:00 2001 From: blattersturm Date: Mon, 21 Oct 2019 15:04:55 +0200 Subject: [PATCH] runcode: improved UI, in-game support, JS support --- resources/runcode/.gitignore | 1 + resources/runcode/__resource.lua | 15 +- resources/runcode/runcode.js | 11 + resources/runcode/runcode_cl.lua | 4 +- resources/runcode/runcode_shared.lua | 23 +- resources/runcode/runcode_sv.lua | 39 ++- resources/runcode/runcode_ui.lua | 66 ++++ resources/runcode/runcode_web.lua | 99 ++++-- resources/runcode/web/index.html | 441 +++++++++++++++++++++++++-- resources/runcode/web/nui.html | 60 ++++ 10 files changed, 702 insertions(+), 57 deletions(-) create mode 100644 resources/runcode/.gitignore create mode 100644 resources/runcode/runcode.js create mode 100644 resources/runcode/runcode_ui.lua create mode 100644 resources/runcode/web/nui.html diff --git a/resources/runcode/.gitignore b/resources/runcode/.gitignore new file mode 100644 index 0000000..114ea57 --- /dev/null +++ b/resources/runcode/.gitignore @@ -0,0 +1 @@ +data.json \ No newline at end of file diff --git a/resources/runcode/__resource.lua b/resources/runcode/__resource.lua index 308f2c9..9fbbfe2 100644 --- a/resources/runcode/__resource.lua +++ b/resources/runcode/__resource.lua @@ -2,7 +2,16 @@ client_script 'runcode_cl.lua' server_script 'runcode_sv.lua' server_script 'runcode_web.lua' -client_script 'runcode_shared.lua' -server_script 'runcode_shared.lua' +shared_script 'runcode_shared.lua' -resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' \ No newline at end of file +shared_script 'runcode.js' + +resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937' + +client_script 'runcode_ui.lua' + +ui_page 'web/nui.html' + +files { + 'web/nui.html' +} \ No newline at end of file diff --git a/resources/runcode/runcode.js b/resources/runcode/runcode.js new file mode 100644 index 0000000..76c878d --- /dev/null +++ b/resources/runcode/runcode.js @@ -0,0 +1,11 @@ +exports('runJS', (snippet) => { + if (IsDuplicityVersion() && GetInvokingResource() !== GetCurrentResourceName()) { + return [ 'Invalid caller.', false ]; + } + + try { + return [ new Function(snippet)(), false ]; + } catch (e) { + return [ false, e.toString() ]; + } +}); \ No newline at end of file diff --git a/resources/runcode/runcode_cl.lua b/resources/runcode/runcode_cl.lua index 3dd5253..29e6806 100644 --- a/resources/runcode/runcode_cl.lua +++ b/resources/runcode/runcode_cl.lua @@ -1,7 +1,7 @@ RegisterNetEvent('runcode:gotSnippet') -AddEventHandler('runcode:gotSnippet', function(id, code) - local res, err = RunCode(code) +AddEventHandler('runcode:gotSnippet', function(id, lang, code) + local res, err = RunCode(lang, code) if not err then if type(res) == 'vector3' then diff --git a/resources/runcode/runcode_shared.lua b/resources/runcode/runcode_shared.lua index b22c388..e1d8111 100644 --- a/resources/runcode/runcode_shared.lua +++ b/resources/runcode/runcode_shared.lua @@ -1,5 +1,12 @@ -function RunCode(code) - local code, err = load(code, '@runcode') +local runners = {} + +function runners.lua(arg) + local code, err = load('return ' .. arg, '@runcode') + + -- if failed, try without return + if err then + code, err = load(arg, '@runcode') + end if err then print(err) @@ -11,7 +18,15 @@ function RunCode(code) if status then return result - else - return nil, result end + + return nil, result +end + +function runners.js(arg) + return table.unpack(exports[GetCurrentResourceName()]:runJS(arg)) +end + +function RunCode(lang, str) + return runners[lang](str) end \ No newline at end of file diff --git a/resources/runcode/runcode_sv.lua b/resources/runcode/runcode_sv.lua index 9fd9e8c..63507e2 100644 --- a/resources/runcode/runcode_sv.lua +++ b/resources/runcode/runcode_sv.lua @@ -1,7 +1,42 @@ +function GetPrivs(source) + return { + canServer = IsPlayerAceAllowed(source, 'command.run'), + canClient = IsPlayerAceAllowed(source, 'command.crun'), + canSelf = IsPlayerAceAllowed(source, 'runcode.self'), + } +end + RegisterCommand('run', function(source, args, rawCommand) - local res, err = RunCode('return ' .. rawCommand:sub(4)) + local res, err = RunCode('lua', rawCommand:sub(4)) end, true) RegisterCommand('crun', function(source, args, rawCommand) - TriggerClientEvent('runcode:gotSnippet', source, -1, 'return ' .. rawCommand:sub(5)) + if not source then + return + end + + TriggerClientEvent('runcode:gotSnippet', source, -1, 'lua', rawCommand:sub(5)) +end, true) + +RegisterCommand('runcode', function(source, args, rawCommand) + if not source then + return + end + + local df = LoadResourceFile(GetCurrentResourceName(), 'data.json') + local saveData = {} + + if df then + saveData = json.decode(df) + end + + local p = GetPrivs(source) + + if not p.canServer and not p.canClient and not p.canSelf then + return + end + + p.saveData = saveData + + TriggerClientEvent('runcode:openUi', source, p) end, true) \ No newline at end of file diff --git a/resources/runcode/runcode_ui.lua b/resources/runcode/runcode_ui.lua new file mode 100644 index 0000000..57bb0fd --- /dev/null +++ b/resources/runcode/runcode_ui.lua @@ -0,0 +1,66 @@ +local openData + +RegisterNetEvent('runcode:openUi') + +AddEventHandler('runcode:openUi', function(options) + openData = { + type = 'open', + options = options, + url = 'http://' .. GetCurrentServerEndpoint() .. '/' .. GetCurrentResourceName() .. '/', + res = GetCurrentResourceName() + } + + SendNuiMessage(json.encode(openData)) +end) + +RegisterNUICallback('getOpenData', function(args, cb) + cb(openData) +end) + +RegisterNUICallback('doOk', function(args, cb) + SendNuiMessage(json.encode({ + type = 'ok' + })) + + SetNuiFocus(true, true) + + cb('ok') +end) + +RegisterNUICallback('doClose', function(args, cb) + SendNuiMessage(json.encode({ + type = 'close' + })) + + SetNuiFocus(false, false) + + cb('ok') +end) + +local rcCbs = {} +local id = 1 + +RegisterNUICallback('runCodeInBand', function(args, cb) + id = id + 1 + + rcCbs[id] = cb + + TriggerServerEvent('runcode:runInBand', id, args) +end) + +RegisterNetEvent('runcode:inBandResult') + +AddEventHandler('runcode:inBandResult', function(id, result) + if rcCbs[id] then + local cb = rcCbs[id] + rcCbs[id] = nil + + cb(result) + end +end) + +AddEventHandler('onResourceStop', function(resourceName) + if resourceName == GetCurrentResourceName() then + SetNuiFocus(false, false) + end +end) \ No newline at end of file diff --git a/resources/runcode/runcode_web.lua b/resources/runcode/runcode_web.lua index 55506dc..9744dba 100644 --- a/resources/runcode/runcode_web.lua +++ b/resources/runcode/runcode_web.lua @@ -21,6 +21,69 @@ end local codeId = 1 local codes = {} +local attempts = 0 +local lastAttempt + +local function handleRunCode(data, res) + if not data.lang then + data.lang = 'lua' + end + + if not data.client or data.client == '' then + CreateThread(function() + local result, err = RunCode(data.lang, data.code) + + res.send(json.encode({ + result = result, + error = err + })) + end) + else + codes[codeId] = { + timeout = GetGameTimer() + 1000, + res = res + } + + TriggerClientEvent('runcode:gotSnippet', tonumber(data.client), codeId, data.lang, data.code) + + codeId = codeId + 1 + end +end + +RegisterNetEvent('runcode:runInBand') + +AddEventHandler('runcode:runInBand', function(id, data) + local s = source + local privs = GetPrivs(s) + + local res = { + send = function(str) + TriggerClientEvent('runcode:inBandResult', s, id, str) + end + } + + if (not data.client or data.client == '') and not privs.canServer then + res.send(json.encode({ error = 'Insufficient permissions.'})) + return + end + + if (data.client and data.client ~= '') and not privs.canClient then + if privs.canSelf then + data.client = s + else + res.send(json.encode({ error = 'Insufficient permissions.'})) + return + end + end + + SaveResourceFile(GetCurrentResourceName(), 'data.json', json.encode({ + lastSnippet = data.code, + lastLang = data.lang or 'lua' + }), -1) + + handleRunCode(data, res) +end) + local function handlePost(req, res) req.setDataHandler(function(body) local data = json.decode(body) @@ -35,33 +98,29 @@ local function handlePost(req, res) return end - if data.password ~= GetConvar('rcon_password', '') then + if attempts > 5 or data.password ~= GetConvar('rcon_password', '') then + attempts = attempts + 1 + lastAttempt = GetGameTimer() + res.send(json.encode({ error = 'Bad password.'})) return end - if not data.client or data.client == '' then - CreateThread(function() - local result, err = RunCode(data.code) - - res.send(json.encode({ - result = result, - error = err - })) - end) - else - codes[codeId] = { - timeout = GetGameTimer() + 1000, - res = res - } - - TriggerClientEvent('runcode:gotSnippet', tonumber(data.client), codeId, data.code) - - codeId = codeId + 1 - end + handleRunCode(data, res) end) end +CreateThread(function() + while true do + Wait(1000) + + if attempts > 0 and (GetGameTimer() - lastAttempt) > 5000 then + attempts = 0 + lastAttempt = 0 + end + end +end) + local function returnCode(id, res, err) if not codes[id] then return diff --git a/resources/runcode/web/index.html b/resources/runcode/web/index.html index 8e52368..c615fca 100644 --- a/resources/runcode/web/index.html +++ b/resources/runcode/web/index.html @@ -6,79 +6,468 @@ + + + -

- Password: (use your rcon password)
- Client ID: (leave blank for server)
-
- -
+
+
+ + +
+
+

+
+

+ + + + +

+
+ +
+
+
+
+ - + \ No newline at end of file