├── utils ├── const.lua ├── server │ └── callback.lua ├── client │ └── callback.lua └── shared.lua ├── locales ├── cs.lua └── en.lua ├── fxmanifest.lua ├── config.lua ├── README.md ├── .gitignore ├── server └── main.lua └── client └── main.lua /utils/const.lua: -------------------------------------------------------------------------------- 1 | Framework = { 2 | ESX = 1, 3 | QBCORE = 2, 4 | } -------------------------------------------------------------------------------- /locales/cs.lua: -------------------------------------------------------------------------------- 1 | Locales["cs"] = { 2 | ["tag_on"] = "Právě jste si zapl tag", 3 | ["tag_off"] = "Právě jste si vypnul tag", 4 | } -------------------------------------------------------------------------------- /locales/en.lua: -------------------------------------------------------------------------------- 1 | Locales["en"] = { 2 | ["tag_on"] = "You just turned on your tag", 3 | ["tag_off"] = "You just turned off your tag", 4 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'adamant' 2 | games {'common'} 3 | 4 | client_scripts { 5 | 'utils/client/callback.lua', 6 | 'client/main.lua', 7 | } 8 | 9 | server_scripts { 10 | 'utils/server/callback.lua', 11 | 'server/main.lua', 12 | } 13 | 14 | shared_scripts { 15 | 'utils/const.lua', 16 | 'config.lua', 17 | 'utils/shared.lua', 18 | 'locales/*.lua', 19 | } -------------------------------------------------------------------------------- /utils/server/callback.lua: -------------------------------------------------------------------------------- 1 | ---TAKEN FROM rcore framework 2 | ---https://githu.com/Isigar/relisoft_core 3 | ---https://docs.rcore.cz 4 | local serverCallbacks = {} 5 | local callbacksRequestsHistory = {} 6 | 7 | function registerCallback(cbName, callback) 8 | serverCallbacks[cbName] = callback 9 | end 10 | 11 | RegisterNetEvent('rcore_tag:callCallback', function(name, requestId, ...) 12 | local source = source 13 | if serverCallbacks[name] == nil then 14 | return 15 | end 16 | callbacksRequestsHistory[requestId] = { 17 | name = name, 18 | source = source, 19 | } 20 | local call = serverCallbacks[name] 21 | call(source, function(...) 22 | TriggerClientEvent('rcore_tag:callCallback', source, requestId, ...) 23 | end, ...) 24 | end) 25 | -------------------------------------------------------------------------------- /utils/client/callback.lua: -------------------------------------------------------------------------------- 1 | ---TAKEN FROM rcore framework 2 | ---https://githu.com/Isigar/relisoft_core 3 | ---https://docs.rcore.cz 4 | 5 | local clientCallbacks = {} 6 | local callbackNames = {} 7 | local currentRequest = 0 8 | 9 | function callCallback(name, cb, ...) 10 | clientCallbacks[currentRequest] = cb 11 | TriggerServerEvent('rcore_tag:callCallback', name, currentRequest, ...) 12 | 13 | if currentRequest < 65535 then 14 | currentRequest = currentRequest + 1 15 | else 16 | currentRequest = 0 17 | end 18 | 19 | callbackNames[currentRequest] = name 20 | end 21 | 22 | RegisterNetEvent('rcore_tag:callCallback', function(requestId, ...) 23 | if clientCallbacks[requestId] == nil then 24 | return 25 | end 26 | clientCallbacks[requestId](...) 27 | end) 28 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | 3 | -- Framework.ESX 4 | -- Framework.QBCORE 5 | Config.Framework = Framework.ESX 6 | 7 | Config.Locale = "en" 8 | 9 | Config.SeeOwnLabel = true 10 | 11 | Config.TextSize = 0.8 12 | Config.Offset = vector3(0, 0, 1.2) 13 | Config.NearCheckWait = 500 14 | 15 | Config.GroupLabels = { 16 | ESX = { 17 | -- group system that used to work on numbers only 18 | [1] = { 19 | [1] = "HELPER", 20 | [2] = "~g~MODERATOR", 21 | [3] = "~b~ADMINISTRATOR", 22 | [4] = "~r~GOD", 23 | [5] = "~r~GOD", 24 | }, 25 | -- group system that works on name 26 | [2] = { 27 | helper = "HELPER", 28 | mod = "~g~MODERATOR", 29 | admin = "~b~ADMINISTRATOR", 30 | superadmin = "~r~GOD", 31 | }, 32 | }, 33 | 34 | QBCore = { 35 | -- group system that works on ACE 36 | [1] = { 37 | god = "~r~GOD", 38 | admin = "~b~ADMINISTRATOR", 39 | mod = "~g~MODERATOR", 40 | }, 41 | } 42 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # relisoft_tag 2 | ### ESX Tag for mods/admin 3 | 4 | **Our discord with more scripts: https://discord.gg/F28PfsY** 5 | 6 | Eazy tag system with configuration 7 | 8 | **Installation** 9 | 1) Put into relisoft_tag folder into your resource folder 10 | 2) Start after es_extended/qbcore in your server.cfg 11 | 3) Edit config.lua file to fit your needs 12 | 13 | **Configurations** 14 | 15 | `Config.SeeOwnLabel = true/false` 16 | - if true you will be see your own tag above yourself 17 | 18 | `Config.TextSize = 1.5` 19 | - font size for tags more is bigger 20 | 21 | `Config.ZOffset = 1.2` 22 | - offset for tag that determinate how much will be tag above player 23 | 24 | `Config.NearCheckWait = 500` 25 | - miliseconds to check if player is near any admin 26 | 27 | 28 | - You can change your labels for admin groups in here 29 | ``` 30 | Config.GroupLabels = { 31 | ESX = { 32 | -- group system that used to work on numbers only 33 | [1] = { 34 | [1] = "HELPER", 35 | [2] = "~g~MODERATOR", 36 | [3] = "~b~ADMINISTRATOR", 37 | [4] = "~r~GOD", 38 | [5] = "~r~GOD", 39 | }, 40 | -- group system that works on name 41 | [1] = { 42 | helper = "HELPER", 43 | mod = "~g~MODERATOR", 44 | admin = "~b~ADMINISTRATOR", 45 | superadmin = "~r~GOD", 46 | }, 47 | }, 48 | 49 | QBCore = { 50 | -- group system that works on ACE 51 | [1] = { 52 | god = "~r~GOD", 53 | admin = "~b~ADMINISTRATOR", 54 | mod = "~g~MODERATOR", 55 | }, 56 | } 57 | } 58 | ``` 59 | - Settings for tag labels, key in table is for group or permission level, you can change it 60 | to fit your needs. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # Generated files 14 | .idea/**/contentModel.xml 15 | 16 | # Sensitive or high-churn files 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/dbnavigator.xml 24 | 25 | # Gradle 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Gradle and Maven with auto-import 30 | # When using Gradle or Maven with auto-import, you should exclude module files, 31 | # since they will be recreated, and may cause churn. Uncomment if using 32 | # auto-import. 33 | # .idea/artifacts 34 | # .idea/compiler.xml 35 | # .idea/jarRepositories.xml 36 | # .idea/modules.xml 37 | # .idea/*.iml 38 | # .idea/modules 39 | # *.iml 40 | # *.ipr 41 | 42 | # CMake 43 | cmake-build-*/ 44 | 45 | # Mongo Explorer plugin 46 | .idea/**/mongoSettings.xml 47 | 48 | # File-based project format 49 | *.iws 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Cursive Clojure plugin 61 | .idea/replstate.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | # Editor-based Rest Client 70 | .idea/httpRequests 71 | 72 | # Android studio 3.1+ serialized cache file 73 | .idea/caches/build_file_checksums.ser 74 | 75 | -------------------------------------------------------------------------------- /server/main.lua: -------------------------------------------------------------------------------- 1 | -- store.rcore.cz 2 | SharedObject = nil 3 | 4 | if Config.Framework == Framework.ESX then 5 | SharedObject = GetEsxObject() 6 | end 7 | 8 | if Config.Framework == Framework.QBCORE then 9 | SharedObject = GetQBCoreObject() 10 | 11 | -- older version of qbcore is running with group name "qbcore" and not "group" this will support both. 12 | for k, v in pairs(SharedObject.Config.Server.Permissions) do 13 | ExecuteCommand(("add_ace qbcore.%s tag.%s allow"):format(v, v)) 14 | ExecuteCommand(("add_ace group.%s tag.%s allow"):format(v, v)) 15 | end 16 | end 17 | 18 | AdminPlayers = {} 19 | 20 | RegisterCommand('tag', function(source, args) 21 | if AdminPlayers[source] == nil then 22 | if Config.Framework == Framework.ESX then 23 | local xPlayer = SharedObject.GetPlayerFromId(source) 24 | if xPlayer.getPermissions then 25 | AdminPlayers[source] = { source = source, permission = xPlayer.getPermissions() } 26 | end 27 | if xPlayer.getGroup then 28 | AdminPlayers[source] = { source = source, group = xPlayer.getGroup() } 29 | end 30 | end 31 | 32 | if Config.Framework == Framework.QBCORE then 33 | for k, v in pairs(SharedObject.Config.Server.Permissions) do 34 | if IsPlayerAceAllowed(source, "tag." .. v) then 35 | AdminPlayers[source] = { source = source, qbcore = v } 36 | break 37 | end 38 | end 39 | end 40 | 41 | TriggerClientEvent('chat:addMessage', source, { args = { 'Tag', _U("tag_on") }, color = { 255, 50, 50 } }) 42 | else 43 | AdminPlayers[source] = nil 44 | TriggerClientEvent('chat:addMessage', source, { args = { 'Tag', _U("tag_off") }, color = { 255, 50, 50 } }) 45 | end 46 | 47 | TriggerClientEvent('relisoft_tag:set_admins', -1, AdminPlayers) 48 | end) 49 | 50 | registerCallback('getAdminsPlayers', function(source, cb) 51 | cb(AdminPlayers) 52 | end) 53 | 54 | AddEventHandler('esx:playerDropped', function(source) 55 | if AdminPlayers[source] ~= nil then 56 | AdminPlayers[source] = nil 57 | end 58 | TriggerClientEvent('relisoft_tag:set_admins', -1, AdminPlayers) 59 | end) -------------------------------------------------------------------------------- /utils/shared.lua: -------------------------------------------------------------------------------- 1 | Locales = {} 2 | 3 | function IsResourceOnServer(resourceName) 4 | if GetResourceState(resourceName) == "started" or GetResourceState(resourceName) == "starting" then 5 | return true 6 | end 7 | return false 8 | end 9 | 10 | function GetEsxObject() 11 | local promise_ = promise:new() 12 | local obj 13 | xpcall(function() 14 | if ESX == nil then 15 | error("ESX variable is nil") 16 | end 17 | obj = ESX 18 | promise_:resolve(obj) 19 | end, function(error) 20 | xpcall(function() 21 | obj = exports["es_extended"]['getSharedObject']() 22 | promise_:resolve(obj) 23 | end, function(error) 24 | TriggerEvent(Config.ESX or "esx:getSharedObject", function(module) 25 | obj = module 26 | promise_:resolve(obj) 27 | end) 28 | end) 29 | end) 30 | 31 | Citizen.Await(obj) 32 | return obj 33 | end 34 | 35 | function GetQBCoreObject() 36 | local promise_ = promise:new() 37 | local obj 38 | xpcall(function() 39 | obj = exports['qb-core']['GetCoreObject']() 40 | promise_:resolve(obj) 41 | end, function(error) 42 | xpcall(function() 43 | obj = exports['qb-core']['GetSharedObject']() 44 | promise_:resolve(obj) 45 | end, function(error) 46 | 47 | local QBCore = nil 48 | local tries = 10 49 | 50 | LoadQBCore = function() 51 | if tries == 0 then 52 | print("The QBCORE couldnt load any object! You need to correct the event / resource name for export!") 53 | return 54 | end 55 | 56 | tries = tries - 1 57 | 58 | if QBCore == nil then 59 | SetTimeout(100, LoadQBCore) 60 | end 61 | 62 | TriggerEvent(Config.QBCoreObject or "QBCore:GetObject", function(module) 63 | QBCore = module 64 | 65 | obj = QBCore 66 | promise_:resolve(QBCore) 67 | end) 68 | end 69 | 70 | LoadQBCore() 71 | 72 | end) 73 | end) 74 | 75 | Citizen.Await(obj) 76 | 77 | return obj 78 | end 79 | 80 | function _U(str, ...) 81 | if type(Locales) ~= "table" then 82 | return string.format("[%s] the locales is wrong type, it is not a table..", GetCurrentResourceName()) 83 | end 84 | if not Locales[Config.Locale] then 85 | return string.format("[%s] The language does not exists: %s", GetCurrentResourceName(), Config.Locale) 86 | end 87 | if not Locales[Config.Locale][str] then 88 | return string.format("[%s] There isnt such [%s] translation", GetCurrentResourceName(), str) 89 | end 90 | return string.format(Locales[Config.Locale][str], ...) 91 | end 92 | 93 | if IsResourceOnServer("es_extended") then 94 | Config.Framework = Framework.ESX 95 | end 96 | if IsResourceOnServer("qb-core") then 97 | Config.Framework = Framework.QBCORE 98 | end 99 | -------------------------------------------------------------------------------- /client/main.lua: -------------------------------------------------------------------------------- 1 | -- store.rcore.cz 2 | 3 | local currentAdminPlayers = {} 4 | local visibleAdmins = {} 5 | local closeAdmins = {} 6 | 7 | RegisterNetEvent('relisoft_tag:set_admins') 8 | AddEventHandler('relisoft_tag:set_admins', function(admins) 9 | currentAdminPlayers = admins 10 | for id, admin in pairs(visibleAdmins) do 11 | if admins[id] == nil then 12 | visibleAdmins[id] = nil 13 | end 14 | end 15 | end) 16 | 17 | CreateThread(function() 18 | callCallback('getAdminsPlayers', function(admins) 19 | currentAdminPlayers = admins 20 | end) 21 | end) 22 | 23 | function draw3DText(pos, text, options) 24 | options = options or {} 25 | local color = options.color or { r = 255, g = 255, b = 255, a = 255 } 26 | local scaleOption = options.size or 0.8 27 | 28 | local camCoords = GetGameplayCamCoords() 29 | local dist = #(camCoords - pos) 30 | local scale = (scaleOption / dist) * 2 31 | local fov = (1 / GetGameplayCamFov()) * 100 32 | local scaleMultiplier = scale * fov 33 | SetDrawOrigin(pos.x, pos.y, pos.z, 0); 34 | SetTextProportional(0) 35 | SetTextScale(0.0 * scaleMultiplier, 0.55 * scaleMultiplier) 36 | SetTextColour(color.r, color.g, color.b, color.a) 37 | SetTextDropshadow(0, 0, 0, 0, 255) 38 | SetTextEdge(2, 0, 0, 0, 150) 39 | SetTextDropShadow() 40 | SetTextOutline() 41 | SetTextEntry("STRING") 42 | SetTextCentre(1) 43 | AddTextComponentString(text) 44 | DrawText(0.0, 0.0) 45 | ClearDrawOrigin() 46 | end 47 | 48 | Citizen.CreateThread(function() 49 | while true do 50 | Wait(Config.NearCheckWait) 51 | local ped = PlayerPedId() 52 | local pedCoords = GetEntityCoords(ped) 53 | for k, v in pairs(currentAdminPlayers) do 54 | local playerServerID = GetPlayerFromServerId(v.source) 55 | if playerServerID ~= -1 then 56 | local adminPed = GetPlayerPed(playerServerID) 57 | local adminCoords = GetEntityCoords(adminPed) 58 | 59 | local distance = #(adminCoords - pedCoords) 60 | if distance < 40 then 61 | visibleAdmins[v.source] = v 62 | else 63 | visibleAdmins[v.source] = nil 64 | end 65 | end 66 | end 67 | end 68 | end) 69 | 70 | CreateThread(function() 71 | while true do 72 | Wait(500) 73 | closeAdmins = {} 74 | for k, v in pairs(visibleAdmins) do 75 | local playerServerID = GetPlayerFromServerId(v.source) 76 | if playerServerID ~= -1 then 77 | local adminPed = GetPlayerPed(playerServerID) 78 | local label 79 | 80 | if v.permission then 81 | label = Config.GroupLabels.ESX[1][v.permission] 82 | end 83 | 84 | if v.group then 85 | label = Config.GroupLabels.ESX[2][v.group] 86 | end 87 | 88 | if v.qbcore then 89 | label = Config.GroupLabels.QBCore[1][v.qbcore] 90 | end 91 | 92 | 93 | if label then 94 | closeAdmins[playerServerID] = { 95 | ped = adminPed, 96 | label = label, 97 | source = v.source, 98 | self = v.source == GetPlayerServerId(PlayerId()), 99 | } 100 | end 101 | end 102 | end 103 | end 104 | end) 105 | 106 | CreateThread(function() 107 | while true do 108 | Wait(0) 109 | if next(closeAdmins) ~= nil then 110 | for k, v in pairs(closeAdmins) do 111 | if v.label then 112 | if v.self then 113 | if Config.SeeOwnLabel == true then 114 | draw3DText(GetEntityCoords(v.ped) + Config.Offset, v.label, { 115 | size = Config.TextSize 116 | }) 117 | end 118 | else 119 | draw3DText(GetEntityCoords(v.ped) + Config.Offset, v.label, { 120 | size = Config.TextSize 121 | }) 122 | end 123 | end 124 | end 125 | else 126 | Wait(1000) 127 | end 128 | end 129 | end) 130 | 131 | --------------------------------------------------------------------------------