diff --git a/client/main.lua b/client/main.lua index 3799766..5240fa5 100644 --- a/client/main.lua +++ b/client/main.lua @@ -15,11 +15,14 @@ lib.registerMenu({ {label = locale('main_options.label2'), description = locale('main_options.desc2'), icon = 'fas fa-user', args = {'qbx_adminmenu_players_menu'}}, {label = locale('main_options.label3'), description = locale('main_options.desc3'), icon = 'fas fa-server', args = {'qbx_adminmenu_server_menu'}}, {label = locale('main_options.label4'), description = locale('main_options.desc4'), icon = 'fas fa-car', args = {'qbx_adminmenu_vehicles_menu'}}, - {label = locale('main_options.label5'), description = locale('main_options.desc5'), icon = 'fas fa-toolbox', args = {'qbx_adminmenu_dev_menu'}} + {label = locale('main_options.label5'), description = locale('main_options.desc5'), icon = 'fas fa-toolbox', args = {'qbx_adminmenu_dev_menu'}}, + {label = locale('main_options.label6'), description = locale('main_options.desc6'), icon = 'fas fa-list', args = {'qbx_adminmenu_reports_menu'}} } }, function(_, _, args) if args[1] == 'qbx_adminmenu_players_menu' then GeneratePlayersMenu() + elseif args[1] == 'qbx_adminmenu_reports_menu' then + GenerateReportMenu() else lib.showMenu(args[1], MenuIndexes[args[1]]) end diff --git a/client/player.lua b/client/player.lua index 31ec743..a39f5de 100644 --- a/client/player.lua +++ b/client/player.lua @@ -136,7 +136,7 @@ function GeneratePlayersMenu() end local optionsList = {} for i = 1, #players do - optionsList[#optionsList + 1] = {label = string.format('ID: %s | Name: %s', players[i].id, players[i].name), description = string.format('CID: %s | %s', players[i].cid, players[i].license), args = {players[i]}} + optionsList[#optionsList + 1] = {label = string.format('ID: %s | Name: %s', players[i].id, players[i].name), description = string.format('CID: %s', players[i].cid), args = {players[i]}} end lib.registerMenu({ id = 'qbx_adminmenu_players_menu', @@ -283,7 +283,7 @@ lib.registerMenu({ } }, function(selected) if selected == 1 then - ExecuteCommand(('viewinv %s'):format(selectedPlayer.id)) + exports.ox_inventory:openInventory('player', selectedPlayer.id) elseif selected == 2 then local succeeded = lib.callback.await('qbx_admin:server:clothingMenu', false, selectedPlayer.id) if succeeded then return end @@ -302,3 +302,7 @@ lib.registerMenu({ exports['pma-voice']:toggleMutePlayer(selectedPlayer.id) end end) + +RegisterNetEvent('qbx_admin:client:killPlayer', function() + SetEntityHealth(cache.ped, 0) +end) \ No newline at end of file diff --git a/client/reports.lua b/client/reports.lua new file mode 100644 index 0000000..70a905d --- /dev/null +++ b/client/reports.lua @@ -0,0 +1,98 @@ +local reportOptions = { + function(report) + lib.alertDialog({ + header = string.format('Report ID: %s | Sender: %s', report.id, report.senderName), + content = report.message, + centered = true, + cancel = false, + size = 'lg', + labels = { + confirm = 'Close' + } + }) + + lib.showMenu(('qbx_adminmenu_reports_menu_%s'):format(report.id), MenuIndexes[('qbx_adminmenu_reports_menu_%s'):format(report.id)]) + end, + function(report) + local input = lib.inputDialog(string.format('Report ID: %s | Sender: %s', report.id, report.senderName), { + {type = 'input', label = 'Reply'} + }) + + if not input then + exports.qbx_core:Notify(locale('error.no_report_reply'), 'error') + else + TriggerServerEvent('qbx_admin:server:sendReply', report, input[1]) + end + + lib.showMenu(('qbx_adminmenu_reports_menu_%s'):format(report.id), MenuIndexes[('qbx_adminmenu_reports_menu_%s'):format(report.id)]) + end, + function(report) + TriggerServerEvent('qbx_admin:server:deleteReport', report) + + GenerateReportMenu() + end +} + +function GenerateReportMenu() + local reports = lib.callback.await('qbx_admin:server:getReports', false) + + if not reports or #reports < 1 then + lib.showMenu('qbx_adminmenu_main_menu', MenuIndexes.qbx_adminmenu_main_menu) + return + end + + local optionsList = {} + + for i = 1, #reports do + optionsList[#optionsList + 1] = {label = string.format('Report ID: %s | Sender: %s', reports[i].id, reports[i].senderName), description = locale('report_options.desc1'), args = {reports[i]}} + end + + lib.registerMenu({ + id = 'qbx_adminmenu_reports_menu', + title = locale('title.reports_menu'), + position = 'top-right', + onClose = function(keyPressed) + CloseMenu(false, keyPressed, 'qbx_adminmenu_main_menu') + end, + onSelected = function(selected) + MenuIndexes.qbx_adminmenu_reports_menu = selected + end, + options = optionsList + }, function(_, _, args) + local report = args[1] + + lib.registerMenu({ + id = ('qbx_adminmenu_reports_menu_%s'):format(report.id), + title = string.format('Report ID: %s | Sender: %s', report.id, report.senderName), + position = 'top-right', + onClose = function(keyPressed) + CloseMenu(false, keyPressed, 'qbx_adminmenu_reports_menu') + end, + onSelected = function(selected) + MenuIndexes[('qbx_adminmenu_reports_menu_%s'):format(report.id)] = selected + end, + options = { + {label = 'View Message', icon = 'fas fa-message'}, + {label = 'Send Message', icon = 'fas fa-reply'}, + {label = 'Close Report', icon = 'fas fa-trash'}, + {label = string.format('Claimed By: %s', report.claimed)}, + {label = string.format('Report ID: %s', report.id)}, + {label = string.format('Sender ID: %s', report.senderId)}, + {label = string.format('Sender Name: %s', report.senderName)} + } + }, function(selected) + local reportOption = reportOptions[selected] + + if not reportOption then + lib.showMenu(('qbx_adminmenu_reports_menu_%s'):format(report.id), MenuIndexes[('qbx_adminmenu_reports_menu_%s'):format(report.id)]) + return + end + + reportOption(report) + end) + + lib.showMenu(('qbx_adminmenu_reports_menu_%s'):format(report.id), MenuIndexes[('qbx_adminmenu_reports_menu_%s'):format(report.id)]) + end) + + lib.showMenu('qbx_adminmenu_reports_menu', MenuIndexes.qbx_adminmenu_reports_menu) +end \ No newline at end of file diff --git a/client/vehicle.lua b/client/vehicle.lua index 2be0b5f..764a052 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -114,33 +114,16 @@ lib.registerMenu({ lib.showMenu('qbx_adminmenu_vehicles_menu', MenuIndexes.qbx_adminmenu_vehicles_menu) return end - local override = { - coords = GetEntityCoords(cache.ped), - heading = GetEntityHeading(cache.ped), - categories = { - mods = true, - repair = true, - armor = true, - respray = true, - liveries = true, - wheels = true, - tint = true, - plate = true, - extras = true, - neons = true, - xenons = true, - horn = true, - turbo = true, - cosmetics = true, - }, - } - TriggerEvent('qb-customs:client:EnterCustoms', override) + + -- New customs needs a way to open it from other resources + exports.qbx_core:Notify('Currently no support for new customs', 'error') elseif selected == 6 then if not cache.vehicle then exports.qbx_core:Notify('You have to be in a vehicle, to use this', 'error') lib.showMenu('qbx_adminmenu_vehicles_menu', MenuIndexes.qbx_adminmenu_vehicles_menu) return end + local dialog = lib.inputDialog('Custom License Plate (Max. 8 characters)', {'License Plate'}) if not dialog or not dialog[1] or dialog[1] == '' then @@ -157,6 +140,7 @@ lib.registerMenu({ end SetVehicleNumberPlateText(cache.vehicle, dialog[1]) + TriggerEvent('qb-vehiclekeys:client:AddKeys', dialog[1]) end end) diff --git a/config/server.lua b/config/server.lua index 8f5ef33..de211bd 100644 --- a/config/server.lua +++ b/config/server.lua @@ -1,6 +1,7 @@ return { commandPerms = { useMenu = 'mod', + reportReply = 'mod', noclip = 'mod', names = 'mod', blips = 'mod', diff --git a/locales/en.json b/locales/en.json index 3fe3a69..6be6b89 100644 --- a/locales/en.json +++ b/locales/en.json @@ -4,7 +4,8 @@ "dev_menu": "Developer Options", "main_menu": "Admin Menu", "players_menu": "Online Players", - "server_menu": "Manage Server" + "server_menu": "Manage Server", + "reports_menu": "Reports" }, "main_options": { "label1": "Admin Options", @@ -16,7 +17,9 @@ "label4": "Vehicles", "desc4": "All about vehicles here", "label5": "Developer Options", - "desc5": "Options that are handy for a developer" + "desc5": "Options that are handy for a developer", + "label6": "Pending Reports", + "desc6": "Someone needs to do it :(" }, "admin_options": { "label1": "Noclip", @@ -141,15 +144,22 @@ } }, + "report_options": { + "desc1": "Click to view report options" + }, "success": { "blips_activated": "Blips activated", "names_activated": "Names activated", "coords_copied": "Coordinates copied to clipboard", - "heading_copied": "Heading copied to clipboard" + "heading_copied": "Heading copied to clipboard", + "report_sent": "Report has been sent", + "new_report": "A new report has been received", + "sent_report_reply": "Your reply has been sent" }, "error": { "no_perms": "You don't have permission to do this", "blips_deactivated": "Blips deactivated", - "names_deactivated": "Names deactivated" + "names_deactivated": "Names deactivated", + "no_report_reply": "Reply failed to send due to no message" } } diff --git a/server/commands.lua b/server/commands.lua index 914b073..727a275 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -1,7 +1,16 @@ local config = require 'config.server'.commandPerms +lib.addCommand('report', { + help = 'Send Report', + params = { + {name = 'report', help = 'Your report message', type = 'string'} + } +}, function(source, args, raw) + SendReport(source, string.sub(raw, 8)) +end) + lib.addCommand('admin', { - help = 'Opens Adminmenu', + help = 'Opens Admin Menu', restricted = config.useMenu, }, function(source) TriggerClientEvent('qbx_admin:client:openMenu', source) diff --git a/server/main.lua b/server/main.lua index 25751d4..be870aa 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,6 +1,42 @@ local config = require 'config.server' local isFrozen = {} +REPORTS = {} + +--- Trigger something on players who have the passed permission +--- @param permission string - The required permission +--- @param cb function - The function, will return player object as parameter +function OnAdmin(permission, cb) + for k, v in pairs(exports.qbx_core:GetQBPlayers()) do + if IsPlayerAceAllowed(k, permission) then + cb(v) + end + end +end + +--- Sends a report to online staff members +--- @param source string - The player's ID +--- @param message string - Message for the report +function SendReport(source, message) + local reportId = #REPORTS + 1 + + REPORTS[reportId] = { + id = reportId, + senderId = source, + senderName = GetPlayerName(source), + message = message, + claimed = 'Nobody' + } + + table.sort(REPORTS, function(a, b) return a.id < b.id end) + + exports.qbx_core:Notify(source, locale('success.report_sent'), 'success') + + OnAdmin(config.commandPerms.reportReply, function(target) + exports.qbx_core:Notify(target.PlayerData.source, locale('success.new_report'), 'success') + end) +end + --- Checks if the source is inside of the target's routingbucket --- if not set the source's routingbucket to the target's --- @param source string - The player's ID @@ -11,9 +47,35 @@ function CheckRoutingbucket(source, target) if sourceBucket ~= targetBucket then SetPlayerRoutingBucket(source, targetBucket) end end +RegisterNetEvent('qbx_admin:server:sendReply', function(report, message) + if not IsPlayerAceAllowed(source, config.commandPerms.reportReply) then exports.qbx_core:Notify(source, locale('error.no_perms'), 'error') return end + + if REPORTS[report.id] then + local name = GetPlayerName(source) + + TriggerClientEvent('chatMessage', report.senderId, "", {255, 0, 0}, string.format('[REPORT #%s] [%s] ^7%s', report.id, name, message)) + + exports.qbx_core:Notify(source, locale('success.sent_report_reply'), 'success') + + if REPORTS[report.id].claimed == 'Nobody' then + REPORTS[report.id].claimed = name + + OnAdmin(config.commandPerms.reportReply, function(target) + exports.qbx_core:Notify(target.PlayerData.source, string.format('Report #%s was claimed by %s', report.id, name), 'success') + end) + end + end +end) + +RegisterNetEvent('qbx_admin:server:deleteReport', function(report) + if not IsPlayerAceAllowed(source, config.commandPerms.reportReply) then exports.qbx_core:Notify(source, locale('error.no_perms'), 'error') return end + + REPORTS[report.id] = nil +end) + local generalOptions = { - function(selectedPlayer) TriggerClientEvent('hospital:client:KillPlayer', selectedPlayer.id) end, - function(selectedPlayer) TriggerClientEvent('hospital:client:Revive', selectedPlayer.id) end, + function(selectedPlayer) TriggerClientEvent('qbx_admin:client:killPlayer', selectedPlayer.id) end, + function(selectedPlayer) TriggerClientEvent('qbx_medical:client:playerRevived', selectedPlayer.id) end, function(selectedPlayer) if isFrozen[selectedPlayer.id] then FreezeEntityPosition(GetPlayerPed(selectedPlayer.id), false) @@ -92,12 +154,10 @@ local playerDataOptions = { crafting = function(target, input) target.Functions.SetMetaData('craftingrep', input[1]) end, dealer = function(target, input) target.Functions.SetMetaData('dealerrep', input[1]) end, cash = function(target, input) - target.PlayerData.moneycash = input[1] - target.Functions.SetPlayerData('money', target.PlayerData.money) + target.Functions.SetMoney('cash', input[1], 'qbx_adminmenu') end, bank = function(target, input) - target.PlayerData.moneybank = input[1] - target.Functions.SetPlayerData('money', target.PlayerData.money) + target.Functions.SetMoney('bank', input[1], 'qbx_adminmenu') end, job = function(target, input) target.Functions.SetJob(input[1], input[2]) @@ -125,7 +185,6 @@ RegisterNetEvent('qbx_admin:server:giveAllWeapons', function(weaponType, playerI if not IsPlayerAceAllowed(source, config.eventPerms.giveAllWeapons) then exports.qbx_core:Notify(source, locale('error.no_perms'), 'error') return end for i = 1, #config.weaponList[weaponType], 1 do - if not exports.ox_inventory:Items()[config.weaponList[weaponType][i]] then return end target.Functions.AddItem(config.weaponList[weaponType][i], 1) end end) @@ -234,3 +293,9 @@ lib.callback.register('qbx_admin:server:spawnVehicle', function(source, model) exports.qbx_vehiclekeys:GiveKeys(source, plate) return netId end) + +lib.callback.register('qbx_admin:server:getReports', function(source) + if not IsPlayerAceAllowed(source, config.commandPerms.reportReply) then exports.qbx_core:Notify(source, locale('error.no_perms'), 'error') return end + + return REPORTS +end) \ No newline at end of file