From 441875e7b357f4c4aa36f0ad2f1fbfb27b8e8cef Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:00:43 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor=20getCoords,=20an?= =?UTF-8?q?d=20fix=20lint=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/[esx]/es_extended/locale.lua | 6 +- .../es_extended/server/classes/player.lua | 105 ++++---- .../[esx]/es_extended/server/functions.lua | 76 ++---- .../[esx]/es_extended/server/main.lua | 250 +++++++++--------- .../es_extended/server/modules/actions.lua | 32 +++ .../es_extended/server/modules/callback.lua | 43 +++ .../[esx]/es_extended/server/modules/npwd.lua | 49 ++++ 7 files changed, 333 insertions(+), 228 deletions(-) create mode 100644 server-data/resources/[esx]/es_extended/server/modules/actions.lua create mode 100644 server-data/resources/[esx]/es_extended/server/modules/callback.lua create mode 100644 server-data/resources/[esx]/es_extended/server/modules/npwd.lua diff --git a/server-data/resources/[esx]/es_extended/locale.lua b/server-data/resources/[esx]/es_extended/locale.lua index af57fcf65..18a1a5ce7 100644 --- a/server-data/resources/[esx]/es_extended/locale.lua +++ b/server-data/resources/[esx]/es_extended/locale.lua @@ -2,8 +2,7 @@ Locales = {} function Translate(str, ...) -- Translate string if not str then - print(("[^1ERROR^7] Resource ^5%s^7 You did not specify a parameter for the Translate function or the value is nil!") - :format(GetInvokingResource() or GetCurrentResourceName())) + print(("[^1ERROR^7] Resource ^5%s^7 You did not specify a parameter for the Translate function or the value is nil!"):format(GetInvokingResource() or GetCurrentResourceName())) return "Given translate function parameter is nil!" end if Locales[Config.Locale] then @@ -26,4 +25,5 @@ function TranslateCap(str, ...) -- Translate string first char uppercase end _ = Translate -_U = TranslateCap +-- luacheck: ignore _U +_U = TranslateCap \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/server/classes/player.lua b/server-data/resources/[esx]/es_extended/server/classes/player.lua index 7562b8fa1..c7149d3e8 100644 --- a/server-data/resources/[esx]/es_extended/server/classes/player.lua +++ b/server-data/resources/[esx]/es_extended/server/classes/player.lua @@ -1,5 +1,6 @@ local _GetPlayerPed = GetPlayerPed local _GetEntityCoords = GetEntityCoords +local _GetEntityHeading = GetEntityHeading local _ExecuteCommand = ExecuteCommand local _SetEntityCoords = SetEntityCoords local _SetEntityHeading = SetEntityHeading @@ -22,10 +23,8 @@ local _assert = assert ---@param name string ---@param coords table | vector4 ---@param metadata table -function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, weight, job, loadout, name, coords, - metadata) - local targetOverrides = Config.PlayerFunctionOverride and Core.PlayerFunctionOverrides - [Config.PlayerFunctionOverride] or {} +function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, weight, job, loadout, name, coords, metadata) + local targetOverrides = Config.PlayerFunctionOverride and Core.PlayerFunctionOverrides[Config.PlayerFunctionOverride] or {} local self = {} @@ -72,20 +71,30 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, ---@return void function self.setCoords(coordinates) local ped = _GetPlayerPed(self.source) - local vector = type(coordinates) == "vector4" and coordinates or - type(coordinates) == "vector3" and vector4(coordinates, 0.0) or - vec(coordinates.x, coordinates.y, coordinates.z, coordinates.heading or 0.0) + local vector = type(coordinates) == "vector4" and coordinates or type(coordinates) == "vector3" and vector4(coordinates, 0.0) or vec(coordinates.x, coordinates.y, coordinates.z, coordinates.heading or 0.0) _SetEntityCoords(ped, vector.xyz, false, false, false, false) _SetEntityHeading(ped, vector.w) end ---@param vector boolean - ---@return vector3 | table - function self.getCoords(vector) + ---@param heading boolean + ---@return vector3 | vector4 | table + function self.getCoords(vector, heading) local ped = _GetPlayerPed(self.source) - local coordinates = _GetEntityCoords(ped) + local entityCoords = _GetEntityCoords(ped) + local entityHeading = _GetEntityHeading(ped) - return vector and coordinates or { x = coordinates.x, y = coordinates.y, z = coordinates.z } + local coordinates = { x = entityCoords.x, y = entityCoords.y, z = entityCoords.z } + + if vector then + coordinates = (heading and vector4(entityCoords.xyz, entityHeading) or entityCoords) + else + if heading then + coordinates.heading = entityHeading + end + end + + return coordinates end ---@param reason string @@ -131,9 +140,16 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, ---@param newGroup string ---@return void function self.setGroup(newGroup) + local lastGroup = self.group + _ExecuteCommand(("remove_principal identifier.%s group.%s"):format(self.license, self.group)) + self.group = newGroup + + _TriggerEvent("esx:setGroup", self.source, self.group, lastGroup) + self.triggerEvent("esx:setGroup", self.group, lastGroup) Player(self.source).state:set("group", self.group, true) + _ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.license, self.group)) end @@ -257,8 +273,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.setAccountMoney(accountName, money, reason) reason = reason or "unknown" if not tonumber(money) then - print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format( - accountName, self.playerId, money)) + print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format(accountName, self.playerId, money)) return end if money >= 0 then @@ -271,12 +286,10 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, self.triggerEvent("esx:setAccountMoney", account) _TriggerEvent("esx:setAccountMoney", self.source, accountName, money, reason) else - print(("[^1ERROR^7] Tried To Set Invalid Account ^5%s^0 For Player ^5%s^0!"):format(accountName, - self.playerId)) + print(("[^1ERROR^7] Tried To Set Invalid Account ^5%s^0 For Player ^5%s^0!"):format(accountName, self.playerId)) end else - print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format( - accountName, self.playerId, money)) + print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format(accountName, self.playerId, money)) end end @@ -287,8 +300,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.addAccountMoney(accountName, money, reason) reason = reason or "Unknown" if not tonumber(money) then - print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format( - accountName, self.playerId, money)) + print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format(accountName, self.playerId, money)) return end if money > 0 then @@ -300,12 +312,10 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, self.triggerEvent("esx:setAccountMoney", account) _TriggerEvent("esx:addAccountMoney", self.source, accountName, money, reason) else - print(("[^1ERROR^7] Tried To Set Add To Invalid Account ^5%s^0 For Player ^5%s^0!"):format(accountName, - self.playerId)) + print(("[^1ERROR^7] Tried To Set Add To Invalid Account ^5%s^0 For Player ^5%s^0!"):format(accountName, self.playerId)) end else - print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format( - accountName, self.playerId, money)) + print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format(accountName, self.playerId, money)) end end @@ -316,8 +326,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.removeAccountMoney(accountName, money, reason) reason = reason or "Unknown" if not tonumber(money) then - print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format( - accountName, self.playerId, money)) + print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format(accountName, self.playerId, money)) return end if money > 0 then @@ -326,8 +335,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, if account then money = account.round and ESX.Math.Round(money) or money if self.accounts[account.index].money - money > self.accounts[account.index].money then - print(("[^1ERROR^7] Tried To Underflow Account ^5%s^0 For Player ^5%s^0!"):format(accountName, - self.playerId)) + print(("[^1ERROR^7] Tried To Underflow Account ^5%s^0 For Player ^5%s^0!"):format(accountName, self.playerId)) return end self.accounts[account.index].money = self.accounts[account.index].money - money @@ -335,12 +343,10 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, self.triggerEvent("esx:setAccountMoney", account) _TriggerEvent("esx:removeAccountMoney", self.source, accountName, money, reason) else - print(("[^1ERROR^7] Tried To Set Add To Invalid Account ^5%s^0 For Player ^5%s^0!"):format(accountName, - self.playerId)) + print(("[^1ERROR^7] Tried To Set Add To Invalid Account ^5%s^0 For Player ^5%s^0!"):format(accountName, self.playerId)) end else - print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format( - accountName, self.playerId, money)) + print(("[^1ERROR^7] Tried To Set Account ^5%s^0 For Player ^5%s^0 To An Invalid Number -> ^5%s^7"):format(accountName, self.playerId, money)) end end @@ -390,8 +396,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, self.triggerEvent("esx:removeInventoryItem", item.name, item.count) end else - print(("[^1ERROR^7] Player ID:^5%s Tried remove a Invalid count -> %s of %s"):format(self.playerId, count, - itemName)) + print(("[^1ERROR^7] Player ID:^5%s Tried remove a Invalid count -> %s of %s"):format(self.playerId, count, itemName)) end end end @@ -471,8 +476,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, local lastJob = self.job if not ESX.DoesJobExist(newJob, grade) then - return print(("[es_extended] [^3WARNING^7] Ignoring invalid ^5.setJob()^7 usage for ID: ^5%s^7, Job: ^5%s^7") - :format(self.source, job)) + return print(("[es_extended] [^3WARNING^7] Ignoring invalid ^5.setJob()^7 usage for ID: ^5%s^7, Job: ^5%s^7"):format(self.source, job)) end local jobObject, gradeObject = ESX.Jobs[newJob], ESX.Jobs[newJob].grades[grade] @@ -491,7 +495,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, skin_female = gradeObject.skin_female and json.decode(gradeObject.skin_female) or {}, } - TriggerEvent("esx:setJob", self.source, self.job, lastJob) + _TriggerEvent("esx:setJob", self.source, self.job, lastJob) self.triggerEvent("esx:setJob", self.job, lastJob) Player(self.source).state:set("job", self.job, true) end @@ -730,8 +734,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, ---@param hudColorIndex number ---@return void function self.showAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex) - self.triggerEvent("esx:showAdvancedNotification", sender, subject, msg, textureDict, iconType, flash, saveToBrief, - hudColorIndex) + self.triggerEvent("esx:showAdvancedNotification", sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex) end ---@param msg string @@ -776,16 +779,14 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, if type(key) == "string" then returnValues[key] = self.getMeta(index, key) else - print(("[^1ERROR^7] xPlayer.getMeta subIndex should be ^5string^7 or ^5table^7! that contains ^5string^7, received ^5%s^7!, skipping...") - :format(type(key))) + print(("[^1ERROR^7] xPlayer.getMeta subIndex should be ^5string^7 or ^5table^7! that contains ^5string^7, received ^5%s^7!, skipping..."):format(type(key))) end end return returnValues end - return print(("[^1ERROR^7] xPlayer.getMeta subIndex should be ^5string^7 or ^5table^7!, received ^5%s^7!") - :format(_type)) + return print(("[^1ERROR^7] xPlayer.getMeta subIndex should be ^5string^7 or ^5table^7!, received ^5%s^7!"):format(_type)) end return metaData @@ -812,8 +813,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, if not subValue then if _type ~= "number" and _type ~= "string" and _type ~= "table" then - return print(("[^1ERROR^7] xPlayer.setMeta ^5%s^7 should be ^5number^7 or ^5string^7 or ^5table^7!") - :format(value)) + return print(("[^1ERROR^7] xPlayer.setMeta ^5%s^7 should be ^5number^7 or ^5string^7 or ^5table^7!"):format(value)) end self.metadata[index] = value @@ -844,8 +844,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, local metaData = self.metadata[index] if metaData == nil then - return Config.EnableDebug and print(("[^1ERROR^7] xPlayer.clearMeta ^5%s^7 does not exist!"):format(index)) or - nil + return Config.EnableDebug and print(("[^1ERROR^7] xPlayer.clearMeta ^5%s^7 does not exist!"):format(index)) or nil end if not subValues then @@ -856,8 +855,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, if type(metaData) == "table" then metaData[subValues] = nil else - return print(("[^1ERROR^7] xPlayer.clearMeta ^5%s^7 is not a table! Cannot clear subValue ^5%s^7.") - :format(index, subValues)) + return print(("[^1ERROR^7] xPlayer.clearMeta ^5%s^7 is not a table! Cannot clear subValue ^5%s^7."):format(index, subValues)) end elseif type(subValues) == "table" then -- If subValues is a table, we will clear multiple subValues within the table @@ -867,17 +865,14 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, if type(metaData) == "table" then metaData[subValue] = nil else - print(("[^1ERROR^7] xPlayer.clearMeta ^5%s^7 is not a table! Cannot clear subValue ^5%s^7.") - :format(index, subValue)) + print(("[^1ERROR^7] xPlayer.clearMeta ^5%s^7 is not a table! Cannot clear subValue ^5%s^7."):format(index, subValue)) end else - print(("[^1ERROR^7] xPlayer.clearMeta subValues should contain ^5string^7, received ^5%s^7, skipping...") - :format(type(subValue))) + print(("[^1ERROR^7] xPlayer.clearMeta subValues should contain ^5string^7, received ^5%s^7, skipping..."):format(type(subValue))) end end else - return print(("[^1ERROR^7] xPlayer.clearMeta ^5subValues^7 should be ^5string^7 or ^5table^7, received ^5%s^7!") - :format(type(subValues))) + return print(("[^1ERROR^7] xPlayer.clearMeta ^5subValues^7 should be ^5string^7 or ^5table^7, received ^5%s^7!"):format(type(subValues))) end Player(self.source).state:set("metadata", self.metadata, true) @@ -888,4 +883,4 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, end return self -end +end \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/server/functions.lua b/server-data/resources/[esx]/es_extended/server/functions.lua index 7990e5c19..007af0b43 100644 --- a/server-data/resources/[esx]/es_extended/server/functions.lua +++ b/server-data/resources/[esx]/es_extended/server/functions.lua @@ -4,6 +4,24 @@ function ESX.Trace(msg) end end +--- Triggers an event for one or more clients. +---@param eventName string The name of the event to trigger. +---@param playerIds table|number If a number, represents a single player ID. If a table, represents an array of player IDs. +---@param ... any Additional arguments to pass to the event handler. +function ESX.TriggerClientEvent(eventName, playerIds, ...) + if type(playerIds) == "number" then + TriggerClientEvent(eventName, playerIds, ...) + return + end + + local payload = msgpack.pack_args(...) + local payloadLength = #payload + + for i = 1, #playerIds do + TriggerClientEventInternal(eventName, playerIds[i], payload, payloadLength) + end +end + function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) if type(name) == "table" then for _, v in ipairs(name) do @@ -106,13 +124,13 @@ function ESX.RegisterCommand(name, group, cb, allowConsole, suggestion) elseif v.type == "any" then newArgs[v.name] = args[k] elseif v.type == "merge" then - local lenght = 0 + local length = 0 for i = 1, k - 1 do - lenght = lenght + string.len(args[i]) + 1 + length = length + string.len(args[i]) + 1 end local merge = table.concat(args, " ") - newArgs[v.name] = string.sub(merge, lenght) + newArgs[v.name] = string.sub(merge, length) elseif v.type == "coordinate" then local coord = tonumber(args[k]:match("(-?%d+%.?%d*)")) if not coord then @@ -181,7 +199,7 @@ function Core.SavePlayer(xPlayer, cb) xPlayer.job.name, xPlayer.job.grade, xPlayer.group, - json.encode(xPlayer.getCoords()), + json.encode(xPlayer.getCoords(false, true)), json.encode(xPlayer.getInventory(true)), json.encode(xPlayer.getLoadout(true)), json.encode(xPlayer.getMeta()), @@ -239,8 +257,7 @@ function Core.SavePlayers(cb) return cb() end - print(("[^2INFO^7] Saved ^5%s^7 %s over ^5%s^7 ms"):format(#parameters, - #parameters > 1 and "players" or "player", ESX.Math.Round((os.time() - startTime) / 1000000, 2))) + print(("[^2INFO^7] Saved ^5%s^7 %s over ^5%s^7 ms"):format(#parameters, #parameters > 1 and "players" or "player", ESX.Math.Round((os.time() - startTime) / 1000000, 2))) end ) end @@ -352,8 +369,7 @@ function ESX.DiscordLog(name, title, color, message) ["color"] = Config.DiscordLogs.Colors[color] or Config.DiscordLogs.Colors.default, ["footer"] = { ["text"] = "| ESX Logs | " .. os.date(), - ["icon_url"] = - "https://cdn.discordapp.com/attachments/944789399852417096/1020099828266586193/blanc-800x800.png", + ["icon_url"] = "https://cdn.discordapp.com/attachments/944789399852417096/1020099828266586193/blanc-800x800.png", }, ["description"] = message, ["author"] = { @@ -384,8 +400,7 @@ function ESX.DiscordLogFields(name, title, color, fields) ["color"] = Config.DiscordLogs.Colors[color] or Config.DiscordLogs.Colors.default, ["footer"] = { ["text"] = "| ESX Logs | " .. os.date(), - ["icon_url"] = - "https://cdn.discordapp.com/attachments/944789399852417096/1020099828266586193/blanc-800x800.png", + ["icon_url"] = "https://cdn.discordapp.com/attachments/944789399852417096/1020099828266586193/blanc-800x800.png", }, ["fields"] = fields, ["description"] = "", @@ -409,41 +424,6 @@ function ESX.DiscordLogFields(name, title, color, fields) ) end ---- Create Job at Runtime ---- @param name string ---- @param label string ---- @param grades table -function ESX.CreateJob(name, label, grades) - if not name then - return print("[^3WARNING^7] missing argument `name(string)` while creating a job") - end - - if not label then - return print("[^3WARNING^7] missing argument `label(string)` while creating a job") - end - - if not grades or not next(grades) then - return print("[^3WARNING^7] missing argument `grades(table)` while creating a job!") - end - - local parameters = {} - local job = { name = name, label = label, grades = {} } - - for _, v in pairs(grades) do - job.grades[tostring(v.grade)] = { job_name = name, grade = v.grade, name = v.name, label = v.label, salary = v - .salary, skin_male = v.skin_male or "{}", skin_female = v.skin_female or "{}" } - parameters[#parameters + 1] = { name, v.grade, v.name, v.label, v.salary, v.skin_male or "{}", v.skin_female or - "{}" } - end - - MySQL.insert("INSERT IGNORE INTO jobs (name, label) VALUES (?, ?)", { name, label }) - MySQL.prepare( - "INSERT INTO job_grades (job_name, grade, name, label, salary, skin_male, skin_female) VALUES (?, ?, ?, ?, ?, ?, ?)", - parameters) - - ESX.Jobs[name] = job -end - function ESX.RefreshJobs() local Jobs = {} local jobs = MySQL.query.await("SELECT * FROM jobs") @@ -490,9 +470,7 @@ function ESX.UseItem(source, item, ...) local success, result = pcall(itemCallback, source, item, ...) if not success then - return result and print(result) or - print(('[^3WARNING^7] An error occured when using item ^5"%s"^7! This was not caused by ESX.'):format( - item)) + return result and print(result) or print(('[^3WARNING^7] An error occured when using item ^5"%s"^7! This was not caused by ESX.'):format(item)) end end else @@ -575,4 +553,4 @@ function Core.IsPlayerAdmin(playerId) end return false -end +end \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/server/main.lua b/server-data/resources/[esx]/es_extended/server/main.lua index fae13115f..0c227d58c 100644 --- a/server-data/resources/[esx]/es_extended/server/main.lua +++ b/server-data/resources/[esx]/es_extended/server/main.lua @@ -3,8 +3,7 @@ SetGameType("BPT FRAMEWORK") local oneSyncState = GetConvar("onesync", "off") local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?" -local loadPlayer = -"SELECT `accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`" +local loadPlayer = "SELECT `accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`" if Config.Multichar then newPlayer = newPlayer .. ", `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?" @@ -27,12 +26,17 @@ if Config.Multichar then end if not ESX.Players[src] then - local identifier = ('%s:%s'):format(char, ESX.GetIdentifier(src)) - return data and createESXPlayer(identifier, src, data) or loadESXPlayer(identifier, src, false) + local identifier = char .. ":" .. ESX.GetIdentifier(src) + if data then + createESXPlayer(identifier, src, data) + else + loadESXPlayer(identifier, src, false) + end end end) else - RegisterNetEvent("esx:onPlayerJoined", function() + RegisterNetEvent("esx:onPlayerJoined") + AddEventHandler("esx:onPlayerJoined", function() local _source = source while not next(ESX.Jobs) do Wait(50) @@ -46,20 +50,25 @@ end function onPlayerJoined(playerId) local identifier = ESX.GetIdentifier(playerId) - - if not identifier then - return DropPlayer(playerId, - "there was an error loading your character!\nError code: identifier-missing-ingame\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") - end - - if ESX.GetPlayerFromIdentifier(identifier) then - return DropPlayer(playerId, - ("there was an error loading your character!\nError code: identifier-active-ingame\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same Rockstar account.\n\nYour Rockstar identifier: %s") - :format(identifier)) + if identifier then + if ESX.GetPlayerFromIdentifier(identifier) then + DropPlayer( + playerId, + ("there was an error loading your character!\nError code: identifier-active-ingame\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same Rockstar account.\n\nYour Rockstar identifier: %s"):format( + identifier + ) + ) + else + local result = MySQL.scalar.await("SELECT 1 FROM users WHERE identifier = ?", { identifier }) + if result then + loadESXPlayer(identifier, playerId, false) + else + createESXPlayer(identifier, playerId) + end + end + else + DropPlayer(playerId, "there was an error loading your character!\nError code: identifier-missing-ingame\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end - - local result = MySQL.scalar.await("SELECT 1 FROM users WHERE identifier = ?", { identifier }) - return result and loadESXPlayer(identifier, playerId, false) or createESXPlayer(identifier, playerId) end function createESXPlayer(identifier, playerId, data) @@ -75,9 +84,7 @@ function createESXPlayer(identifier, playerId, data) defaultGroup = "admin" end - local parameters = Config.Multichar and - { json.encode(accounts), identifier, defaultGroup, data.firstname, data.lastname, data.dateofbirth, data.sex, data - .height } or { json.encode(accounts), identifier, defaultGroup } + local parameters = Config.Multichar and { json.encode(accounts), identifier, defaultGroup, data.firstname, data.lastname, data.dateofbirth, data.sex, data.height } or { json.encode(accounts), identifier, defaultGroup } if Config.StartingInventoryItems then table.insert(parameters, json.encode(Config.StartingInventoryItems)) @@ -95,22 +102,23 @@ if not Config.Multichar then local identifier = ESX.GetIdentifier(playerId) if oneSyncState == "off" or oneSyncState == "legacy" then - return deferrals.done(("[ESX] ESX Requires Onesync Infinity to work. This server currently has Onesync set to: %s") - :format(oneSyncState)) + return deferrals.done(("[ESX] ESX Requires Onesync Infinity to work. This server currently has Onesync set to: %s"):format(oneSyncState)) end if not Core.DatabaseConnected then - return deferrals.done( - "[ESX] OxMySQL Was Unable To Connect to your database. Please make sure it is turned on and correctly configured in your server.cfg") + return deferrals.done("[ESX] OxMySQL Was Unable To Connect to your database. Please make sure it is turned on and correctly configured in your server.cfg") end if identifier then - return ESX.GetPlayerFromIdentifier(identifier) and - deferrals.done(("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s") - :format(identifier)) or deferrals.done() + if ESX.GetPlayerFromIdentifier(identifier) then + return deferrals.done( + ("[ESX] There was an error loading your character!\nError code: identifier-active\n\nThis error is caused by a player on this server who has the same identifier as you have. Make sure you are not playing on the same account.\n\nYour identifier: %s"):format(identifier) + ) + else + return deferrals.done() + end else - return deferrals.done( - "[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") + return deferrals.done("[ESX] There was an error loading your character!\nError code: identifier-missing\n\nThe cause of this error is not known, your identifier could not be found. Please come back later or report this problem to the server administration team.") end end) end @@ -132,7 +140,8 @@ function loadESXPlayer(identifier, playerId, isNew) local result = MySQL.prepare.await(loadPlayer, { identifier }) -- Accounts - local accounts = (result.accounts and result.accounts ~= "") and json.decode(result.accounts) or {} + local accounts = result.accounts + accounts = (accounts and accounts ~= "") and json.decode(accounts) or {} for account, data in pairs(Config.Accounts) do data.round = data.round or data.round == nil @@ -151,8 +160,7 @@ function loadESXPlayer(identifier, playerId, isNew) local job, grade = result.job, tostring(result.job_grade) if not ESX.DoesJobExist(job, grade) then - print(("[^3WARNING^7] Ignoring invalid job for ^5%s^7 [job: ^5%s^7, grade: ^5%s^7]"):format(identifier, job, - grade)) + print(("[^3WARNING^7] Ignoring invalid job for ^5%s^7 [job: ^5%s^7, grade: ^5%s^7]"):format(identifier, job, grade)) job, grade = "unemployed", "0" end @@ -231,18 +239,16 @@ function loadESXPlayer(identifier, playerId, isNew) end -- Position - userData.coords = json.decode(result.position) or Config.DefaultSpawns[math.random(#Config.DefaultSpawns)] + userData.coords = json.decode(result.position) or Config.DefaultSpawns[ESX.Math.Random(1,#Config.DefaultSpawns)] -- Skin - userData.skin = (result.skin and result.skin ~= "") and json.decode(result.skin) or - { sex = userData.sex == "f" and 1 or 0 } + userData.skin = (result.skin and result.skin ~= "") and json.decode(result.skin) or { sex = userData.sex == "f" and 1 or 0 } -- Metadata userData.metadata = (result.metadata and result.metadata ~= "") and json.decode(result.metadata) or {} -- xPlayer Creation - local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, - userData.weight, userData.job, userData.loadout, GetPlayerName(playerId), userData.coords, userData.metadata) + local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, GetPlayerName(playerId), userData.coords, userData.metadata) ESX.Players[playerId] = xPlayer Core.playersByIdentifier[identifier] = xPlayer @@ -290,7 +296,6 @@ function loadESXPlayer(identifier, playerId, isNew) end end end - xPlayer.triggerEvent("esx:registerSuggestions", Core.RegisteredCommands) print(('[^2INFO^0] Player ^5"%s"^0 has connected to the server. ID: ^5%s^7'):format(xPlayer.getName(), playerId)) end @@ -358,20 +363,24 @@ AddEventHandler("esx:playerLogout", function(playerId, cb) end) if not Config.OxInventory then - RegisterNetEvent("esx:updateWeaponAmmo", function(weaponName, ammoCount) + RegisterNetEvent("esx:updateWeaponAmmo") + AddEventHandler("esx:updateWeaponAmmo", function(weaponName, ammoCount) local xPlayer = ESX.GetPlayerFromId(source) - return xPlayer and xPlayer.updateWeaponAmmo(weaponName, ammoCount) + if xPlayer then + xPlayer.updateWeaponAmmo(weaponName, ammoCount) + end end) - RegisterNetEvent("esx:giveInventoryItem", function(target, itemType, itemName, itemCount) + RegisterNetEvent("esx:giveInventoryItem") + AddEventHandler("esx:giveInventoryItem", function(target, itemType, itemName, itemCount) local playerId = source local sourceXPlayer = ESX.GetPlayerFromId(playerId) local targetXPlayer = ESX.GetPlayerFromId(target) local distance = #(GetEntityCoords(GetPlayerPed(playerId)) - GetEntityCoords(GetPlayerPed(target))) - if not sourceXPlayer or not targetXPlayer or distance > Config.DistanceGive then - return print(("[^3WARNING^7] Player Detected Cheating: ^5%s^7"):format(GetPlayerName(playerId))) + print(("[^3WARNING^7] Player Detected Cheating: ^5%s^7"):format(GetPlayerName(playerId))) + return end if itemType == "item_standard" then @@ -382,10 +391,8 @@ if not Config.OxInventory then sourceXPlayer.removeInventoryItem(itemName, itemCount) targetXPlayer.addInventoryItem(itemName, itemCount) - sourceXPlayer.showNotification(TranslateCap("gave_item", itemCount, sourceItem.label, - targetXPlayer.name)) - targetXPlayer.showNotification(TranslateCap("received_item", itemCount, sourceItem.label, - sourceXPlayer.name)) + sourceXPlayer.showNotification(TranslateCap("gave_item", itemCount, sourceItem.label, targetXPlayer.name)) + targetXPlayer.showNotification(TranslateCap("received_item", itemCount, sourceItem.label, sourceXPlayer.name)) else sourceXPlayer.showNotification(TranslateCap("ex_inv_lim", targetXPlayer.name)) end @@ -397,10 +404,8 @@ if not Config.OxInventory then sourceXPlayer.removeAccountMoney(itemName, itemCount, "Gave to " .. targetXPlayer.name) targetXPlayer.addAccountMoney(itemName, itemCount, "Received from " .. sourceXPlayer.name) - sourceXPlayer.showNotification(TranslateCap("gave_account_money", ESX.Math.GroupDigits(itemCount), - Config.Accounts[itemName].label, targetXPlayer.name)) - targetXPlayer.showNotification(TranslateCap("received_account_money", ESX.Math.GroupDigits(itemCount), - Config.Accounts[itemName].label, sourceXPlayer.name)) + sourceXPlayer.showNotification(TranslateCap("gave_account_money", ESX.Math.GroupDigits(itemCount), Config.Accounts[itemName].label, targetXPlayer.name)) + targetXPlayer.showNotification(TranslateCap("received_account_money", ESX.Math.GroupDigits(itemCount), Config.Accounts[itemName].label, sourceXPlayer.name)) else sourceXPlayer.showNotification(TranslateCap("imp_invalid_amount")) end @@ -426,18 +431,15 @@ if not Config.OxInventory then if weaponObject.ammo and itemCount > 0 then local ammoLabel = weaponObject.ammo.label - sourceXPlayer.showNotification(TranslateCap("gave_weapon_withammo", weaponLabel, itemCount, - ammoLabel, targetXPlayer.name)) - targetXPlayer.showNotification(TranslateCap("received_weapon_withammo", weaponLabel, itemCount, - ammoLabel, sourceXPlayer.name)) + sourceXPlayer.showNotification(TranslateCap("gave_weapon_withammo", weaponLabel, itemCount, ammoLabel, targetXPlayer.name)) + targetXPlayer.showNotification(TranslateCap("received_weapon_withammo", weaponLabel, itemCount, ammoLabel, sourceXPlayer.name)) else sourceXPlayer.showNotification(TranslateCap("gave_weapon", weaponLabel, targetXPlayer.name)) targetXPlayer.showNotification(TranslateCap("received_weapon", weaponLabel, sourceXPlayer.name)) end else sourceXPlayer.showNotification(TranslateCap("gave_weapon_hasalready", targetXPlayer.name, weaponLabel)) - targetXPlayer.showNotification(TranslateCap("received_weapon_hasalready", sourceXPlayer.name, - weaponLabel)) + targetXPlayer.showNotification(TranslateCap("received_weapon_hasalready", sourceXPlayer.name, weaponLabel)) end end elseif itemType == "item_ammo" then @@ -454,16 +456,13 @@ if not Config.OxInventory then sourceXPlayer.removeWeaponAmmo(itemName, itemCount) targetXPlayer.addWeaponAmmo(itemName, itemCount) - sourceXPlayer.showNotification(TranslateCap("gave_weapon_ammo", itemCount, ammoLabel, - weapon.label, targetXPlayer.name)) - targetXPlayer.showNotification(TranslateCap("received_weapon_ammo", itemCount, ammoLabel, - weapon.label, sourceXPlayer.name)) + sourceXPlayer.showNotification(TranslateCap("gave_weapon_ammo", itemCount, ammoLabel, weapon.label, targetXPlayer.name)) + targetXPlayer.showNotification(TranslateCap("received_weapon_ammo", itemCount, ammoLabel, weapon.label, sourceXPlayer.name)) end end else sourceXPlayer.showNotification(TranslateCap("gave_weapon_noweapon", targetXPlayer.name)) - targetXPlayer.showNotification(TranslateCap("received_weapon_noweapon", sourceXPlayer.name, - weapon.label)) + targetXPlayer.showNotification(TranslateCap("received_weapon_noweapon", sourceXPlayer.name, weapon.label)) end end end @@ -499,11 +498,9 @@ if not Config.OxInventory then xPlayer.showNotification(TranslateCap("imp_invalid_amount")) else xPlayer.removeAccountMoney(itemName, itemCount, "Threw away") - local pickupLabel = ("%s [%s]"):format(account.label, - TranslateCap("locale_currency", ESX.Math.GroupDigits(itemCount))) + local pickupLabel = ("%s [%s]"):format(account.label, TranslateCap("locale_currency", ESX.Math.GroupDigits(itemCount))) ESX.CreatePickup("item_account", itemName, itemCount, pickupLabel, playerId) - xPlayer.showNotification(TranslateCap("threw_account", ESX.Math.GroupDigits(itemCount), - string.lower(account.label))) + xPlayer.showNotification(TranslateCap("threw_account", ESX.Math.GroupDigits(itemCount), string.lower(account.label))) end end elseif itemType == "item_weapon" then @@ -512,70 +509,77 @@ if not Config.OxInventory then if xPlayer.hasWeapon(itemName) then local _, weapon = xPlayer.getWeapon(itemName) local _, weaponObject = ESX.GetWeapon(itemName) - local components, pickupLabel = ESX.Table.Clone(weapon.components) + -- luacheck: ignore weaponPickupLabel + local weaponPickupLabel = "" + local components = ESX.Table.Clone(weapon.components) xPlayer.removeWeapon(itemName) if weaponObject.ammo and weapon.ammo > 0 then local ammoLabel = weaponObject.ammo.label - pickupLabel = ("%s [%s %s]"):format(weapon.label, weapon.ammo, ammoLabel) + weaponPickupLabel = ("%s [%s %s]"):format(weapon.label, weapon.ammo, ammoLabel) xPlayer.showNotification(TranslateCap("threw_weapon_ammo", weapon.label, weapon.ammo, ammoLabel)) else - pickupLabel = ("%s"):format(weapon.label) + weaponPickupLabel = ("%s"):format(weapon.label) xPlayer.showNotification(TranslateCap("threw_weapon", weapon.label)) end - ESX.CreatePickup("item_weapon", itemName, weapon.ammo, pickupLabel, playerId, components, - weapon.tintIndex) + ESX.CreatePickup("item_weapon", itemName, weapon.ammo, weaponPickupLabel, playerId, components, weapon.tintIndex) end end end) - RegisterNetEvent("esx:useItem", function(itemName) + RegisterNetEvent("esx:useItem") + AddEventHandler("esx:useItem", function(itemName) local source = source local xPlayer = ESX.GetPlayerFromId(source) local count = xPlayer.getInventoryItem(itemName).count - return count > 0 and ESX.UseItem(source, itemName) or xPlayer.showNotification(TranslateCap('act_imp')) + if count > 0 then + ESX.UseItem(source, itemName) + else + xPlayer.showNotification(TranslateCap("act_imp")) + end end) - RegisterNetEvent("esx:onPickup", function(pickupId) + RegisterNetEvent("esx:onPickup") + AddEventHandler("esx:onPickup", function(pickupId) local pickup, xPlayer, success = Core.Pickups[pickupId], ESX.GetPlayerFromId(source) - if not pickup then return end - - local playerPickupDistance = #(pickup.coords - xPlayer.getCoords(true)) - if playerPickupDistance > 5.0 then - return print(("[^3WARNING^7] Player Detected Cheating (Out of range pickup): ^5%s^7"):format(xPlayer - .getIdentifier())) - end - - if pickup.type == "item_standard" then - if xPlayer.canCarryItem(pickup.name, pickup.count) then - xPlayer.addInventoryItem(pickup.name, pickup.count) - success = true - else - xPlayer.showNotification(TranslateCap("threw_cannot_pickup")) + if pickup then + local playerPickupDistance = #(pickup.coords - xPlayer.getCoords(true)) + if playerPickupDistance > 5.0 then + print(("[^3WARNING^7] Player Detected Cheating (Out of range pickup): ^5%s^7"):format(xPlayer.getIdentifier())) + return end - elseif pickup.type == "item_account" then - success = true - xPlayer.addAccountMoney(pickup.name, pickup.count, "Picked up") - elseif pickup.type == "item_weapon" then - if xPlayer.hasWeapon(pickup.name) then - xPlayer.showNotification(TranslateCap("threw_weapon_already")) - else + + if pickup.type == "item_standard" then + if xPlayer.canCarryItem(pickup.name, pickup.count) then + xPlayer.addInventoryItem(pickup.name, pickup.count) + success = true + else + xPlayer.showNotification(TranslateCap("threw_cannot_pickup")) + end + elseif pickup.type == "item_account" then success = true - xPlayer.addWeapon(pickup.name, pickup.count) - xPlayer.setWeaponTint(pickup.name, pickup.tintIndex) + xPlayer.addAccountMoney(pickup.name, pickup.count, "Picked up") + elseif pickup.type == "item_weapon" then + if xPlayer.hasWeapon(pickup.name) then + xPlayer.showNotification(TranslateCap("threw_weapon_already")) + else + success = true + xPlayer.addWeapon(pickup.name, pickup.count) + xPlayer.setWeaponTint(pickup.name, pickup.tintIndex) - for _, v in ipairs(pickup.components) do - xPlayer.addWeaponComponent(pickup.name, v) + for _, v in ipairs(pickup.components) do + xPlayer.addWeaponComponent(pickup.name, v) + end end end - end - if success then - Core.Pickups[pickupId] = nil - TriggerClientEvent("esx:removePickup", -1, pickupId) + if success then + Core.Pickups[pickupId] = nil + TriggerClientEvent("esx:removePickup", -1, pickupId) + end end end) end @@ -600,7 +604,7 @@ ESX.RegisterServerCallback("esx:isUserAdmin", function(source, cb) end) ESX.RegisterServerCallback("esx:getGameBuild", function(_, cb) - cb(tonumber(GetConvarInt("sv_enforceGameBuild", 1604))) + cb(tonumber(GetConvar("sv_enforceGameBuild", 1604))) end) ESX.RegisterServerCallback("esx:getOtherPlayerData", function(_, cb, target) @@ -624,7 +628,11 @@ ESX.RegisterServerCallback("esx:getPlayerNames", function(source, cb, players) for playerId, _ in pairs(players) do local xPlayer = ESX.GetPlayerFromId(playerId) - players[playerId] = xPlayer and xPlayer.getName() or nil + if xPlayer then + players[playerId] = xPlayer.getName() + else + players[playerId] = nil + end end cb(players) @@ -632,8 +640,7 @@ end) ESX.RegisterServerCallback("esx:spawnVehicle", function(source, cb, vehData) local ped = GetPlayerPed(source) - ESX.OneSync.SpawnVehicle(vehData.model or `ADDER`, vehData.coords or GetEntityCoords(ped), vehData.coords.w or 0.0, - vehData.props or {}, function(id) + ESX.OneSync.SpawnVehicle(vehData.model or `ADDER`, vehData.coords or GetEntityCoords(ped), vehData.coords.w or 0.0, vehData.props or {}, function(id) if vehData.warp then local vehicle = NetworkGetEntityFromNetworkId(id) local timeout = 0 @@ -643,23 +650,24 @@ ESX.RegisterServerCallback("esx:spawnVehicle", function(source, cb, vehData) timeout += 1 end end - cb(id) end) end) AddEventHandler("txAdmin:events:scheduledRestart", function(eventData) - if eventData.secondsRemaining ~= 60 then return end - - CreateThread(function() - Wait(50000) - Core.SavePlayers() - end) + if eventData.secondsRemaining == 60 then + CreateThread(function() + Wait(50000) + Core.SavePlayers() + end) + end end) -AddEventHandler("txAdmin:events:serverShuttingDown", Core.SavePlayers) +AddEventHandler("txAdmin:events:serverShuttingDown", function() + Core.SavePlayers() +end) -local doNotUse = { +local DoNotUse = { ["essentialmode"] = true, ["es_admin2"] = true, ["basic-gamemode"] = true, @@ -671,7 +679,7 @@ local doNotUse = { } AddEventHandler("onResourceStart", function(key) - if doNotUse[string.lower(key)] then + if DoNotUse[string.lower(key)] then while GetResourceState(key) ~= "started" do Wait(0) end @@ -681,9 +689,9 @@ AddEventHandler("onResourceStart", function(key) end end) -for key in pairs(doNotUse) do +for key in pairs(DoNotUse) do if GetResourceState(key) == "started" or GetResourceState(key) == "starting" then StopResource(key) print(("[^1ERROR^7] WE STOPPED A RESOURCE THAT WILL BREAK ^1ESX^7, PLEASE REMOVE ^5%s^7"):format(key)) end -end +end \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/server/modules/actions.lua b/server-data/resources/[esx]/es_extended/server/modules/actions.lua new file mode 100644 index 000000000..4ff53b737 --- /dev/null +++ b/server-data/resources/[esx]/es_extended/server/modules/actions.lua @@ -0,0 +1,32 @@ +RegisterServerEvent("esx:playerPedChanged") +RegisterServerEvent("esx:playerJumping") +RegisterServerEvent("esx:enteringVehicle") +RegisterServerEvent("esx:enteringVehicleAborted") +RegisterServerEvent("esx:enteredVehicle") +RegisterServerEvent("esx:exitedVehicle") + +if Config.EnableDebug then + AddEventHandler("esx:playerPedChanged", function(netId) + print("esx:playerPedChanged", source, netId) + end) + + AddEventHandler("esx:playerJumping", function() + print("esx:playerJumping", source) + end) + + AddEventHandler("esx:enteringVehicle", function(plate, seat, netId) + print("esx:enteringVehicle", "source", source, "plate", plate, "seat", seat, "netId", netId) + end) + + AddEventHandler("esx:enteringVehicleAborted", function() + print("esx:enteringVehicleAborted", source) + end) + + AddEventHandler("esx:enteredVehicle", function(plate, seat, displayName, netId) + print("esx:enteredVehicle", "source", source, "plate", plate, "seat", seat, "displayName", displayName, "netId", netId) + end) + + AddEventHandler("esx:exitedVehicle", function(plate, seat, displayName, netId) + print("esx:exitedVehicle", "source", source, "plate", plate, "seat", seat, "displayName", displayName, "netId", netId) + end) +end \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/server/modules/callback.lua b/server-data/resources/[esx]/es_extended/server/modules/callback.lua new file mode 100644 index 000000000..53368cb83 --- /dev/null +++ b/server-data/resources/[esx]/es_extended/server/modules/callback.lua @@ -0,0 +1,43 @@ +local serverCallbacks = {} + +local clientRequests = {} +local RequestId = 0 + +---@param eventName string +---@param callback function +ESX.RegisterServerCallback = function(eventName, callback) + serverCallbacks[eventName] = callback +end + +RegisterNetEvent("esx:triggerServerCallback", function(eventName, requestId, invoker, ...) + if not serverCallbacks[eventName] then + return print(("[^1ERROR^7] Server Callback not registered, name: ^5%s^7, invoker resource: ^5%s^7"):format(eventName, invoker)) + end + + local source = source + + serverCallbacks[eventName](source, function(...) + TriggerClientEvent("esx:serverCallback", source, requestId, invoker, ...) + end, ...) +end) + +---@param player number playerId +---@param eventName string +---@param callback function +---@param ... any +ESX.TriggerClientCallback = function(player, eventName, callback, ...) + clientRequests[RequestId] = callback + + TriggerClientEvent("esx:triggerClientCallback", player, eventName, RequestId, GetInvokingResource() or "unknown", ...) + + RequestId = RequestId + 1 +end + +RegisterNetEvent("esx:clientCallback", function(requestId, invoker, ...) + if not clientRequests[requestId] then + return print(("[^1ERROR^7] Client Callback with requestId ^5%s^7 Was Called by ^5%s^7 but does not exist."):format(requestId, invoker)) + end + + clientRequests[requestId](...) + clientRequests[requestId] = nil +end) \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/server/modules/npwd.lua b/server-data/resources/[esx]/es_extended/server/modules/npwd.lua new file mode 100644 index 000000000..713d32580 --- /dev/null +++ b/server-data/resources/[esx]/es_extended/server/modules/npwd.lua @@ -0,0 +1,49 @@ +local npwd = GetResourceState("npwd"):find("start") and exports.npwd or nil + +AddEventHandler("onServerResourceStart", function(resource) + if resource ~= "npwd" then + return + end + + npwd = GetResourceState("npwd"):find("start") and exports.npwd or nil + + for _, xPlayer in pairs(ESX.Players) do + npwd:newPlayer({ + source = xPlayer.source, + identifier = xPlayer.identifier, + firstname = xPlayer.get("firstName"), + lastname = xPlayer.get("lastName"), + }) + end +end) + +AddEventHandler("onServerResourceStop", function(resource) + if resource == "npwd" then + npwd = nil + end +end) + +AddEventHandler("esx:playerLoaded", function(playerId, xPlayer) + if not npwd then + return + end + + if not xPlayer then + xPlayer = ESX.GetPlayerFromId(playerId) + end + + npwd:newPlayer({ + source = playerId, + identifier = xPlayer.identifier, + firstname = xPlayer.get("firstName"), + lastname = xPlayer.get("lastName"), + }) +end) + +AddEventHandler("esx:playerLogout", function(playerId) + if not npwd then + return + end + + npwd:unloadPlayer(playerId) +end) \ No newline at end of file