773 lines
28 KiB
Lua
773 lines
28 KiB
Lua
SetMapName("San Andreas")
|
|
SetGameType("ESX Legacy")
|
|
|
|
local oneSyncState = GetConvar("onesync", "off")
|
|
local newPlayer = "INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `ssn` = ?, `group` = ?"
|
|
local loadPlayer = "SELECT `accounts`, `ssn`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`, `metadata`"
|
|
|
|
if Config.Multichar then
|
|
newPlayer = newPlayer .. ", `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?"
|
|
end
|
|
|
|
if Config.StartingInventoryItems then
|
|
newPlayer = newPlayer .. ", `inventory` = ?"
|
|
end
|
|
|
|
if Config.Multichar or Config.Identity then
|
|
loadPlayer = loadPlayer .. ", `firstname`, `lastname`, `dateofbirth`, `sex`, `height`"
|
|
end
|
|
|
|
loadPlayer = loadPlayer .. " FROM `users` WHERE identifier = ?"
|
|
|
|
local function createESXPlayer(identifier, playerId, data)
|
|
local accounts = {}
|
|
|
|
for account, money in pairs(Config.StartingAccountMoney) do
|
|
accounts[account] = money
|
|
end
|
|
|
|
local defaultGroup = "user"
|
|
if Core.IsPlayerAdmin(playerId) then
|
|
print(("[^2INFO^0] Player ^5%s^0 Has been granted admin permissions via ^5Ace Perms^7."):format(playerId))
|
|
defaultGroup = "admin"
|
|
end
|
|
local parameters = Config.Multichar and
|
|
{ json.encode(accounts), identifier, Core.generateSSN(), defaultGroup, data.firstname, data.lastname, data.dateofbirth, data.sex, data.height }
|
|
or { json.encode(accounts), identifier, Core.generateSSN(), defaultGroup }
|
|
|
|
if Config.StartingInventoryItems then
|
|
table.insert(parameters, json.encode(Config.StartingInventoryItems))
|
|
end
|
|
|
|
MySQL.prepare(newPlayer, parameters, function()
|
|
loadESXPlayer(identifier, playerId, true)
|
|
end)
|
|
end
|
|
|
|
|
|
local 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
|
|
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
|
|
end
|
|
|
|
---@param playerId number
|
|
---@param reason string
|
|
---@param cb function?
|
|
local function onPlayerDropped(playerId, reason, cb)
|
|
local p = not cb and promise:new()
|
|
local function resolve()
|
|
if cb then
|
|
return cb()
|
|
elseif(p) then
|
|
return p:resolve()
|
|
end
|
|
end
|
|
|
|
local xPlayer = ESX.GetPlayerFromId(playerId)
|
|
if not xPlayer then
|
|
return resolve()
|
|
end
|
|
|
|
TriggerEvent("esx:playerDropped", playerId, reason)
|
|
local job = xPlayer.getJob().name
|
|
local currentJob = Core.JobsPlayerCount[job]
|
|
Core.JobsPlayerCount[job] = ((currentJob and currentJob > 0) and currentJob or 1) - 1
|
|
|
|
GlobalState[("%s:count"):format(job)] = Core.JobsPlayerCount[job]
|
|
|
|
Core.SavePlayer(xPlayer, function()
|
|
GlobalState["playerCount"] = GlobalState["playerCount"] - 1
|
|
ESX.Players[playerId] = nil
|
|
Core.playersByIdentifier[xPlayer.identifier] = nil
|
|
|
|
resolve()
|
|
end)
|
|
|
|
if p then
|
|
return Citizen.Await(p)
|
|
end
|
|
end
|
|
AddEventHandler("esx:onPlayerDropped", onPlayerDropped)
|
|
|
|
|
|
if Config.Multichar then
|
|
AddEventHandler("esx:onPlayerJoined", function(src, char, data)
|
|
while not next(ESX.Jobs) do
|
|
Wait(50)
|
|
end
|
|
|
|
if not ESX.Players[src] then
|
|
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()
|
|
local _source = source
|
|
while not next(ESX.Jobs) do
|
|
Wait(50)
|
|
end
|
|
|
|
if not ESX.Players[_source] then
|
|
onPlayerJoined(_source)
|
|
end
|
|
end)
|
|
end
|
|
|
|
if not Config.Multichar then
|
|
AddEventHandler("playerConnecting", function(_, _, deferrals)
|
|
local playerId = source
|
|
deferrals.defer()
|
|
Wait(0) -- Required
|
|
local identifier
|
|
local correctLicense, _ = pcall(function ()
|
|
identifier = ESX.GetIdentifier(playerId)
|
|
end)
|
|
|
|
-- luacheck: ignore
|
|
if not SetEntityOrphanMode then
|
|
return deferrals.done(("[ESX] ESX Requires a minimum Artifact version of 10188, Please update your server."))
|
|
end
|
|
|
|
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))
|
|
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")
|
|
end
|
|
|
|
if not identifier or not correctLicense then
|
|
if GetResourceState("esx_identity") ~= "started" then
|
|
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
|
|
|
|
local xPlayer = ESX.GetPlayerFromIdentifier(identifier)
|
|
|
|
if not xPlayer then
|
|
return deferrals.done()
|
|
end
|
|
|
|
if GetPlayerPing(xPlayer.source --[[@as string]]) > 0 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)
|
|
)
|
|
end
|
|
|
|
deferrals.update(("[ESX] Cleaning stale player entry..."):format(identifier))
|
|
onPlayerDropped(xPlayer.source, "esx_stale_player_obj")
|
|
deferrals.done()
|
|
end)
|
|
end
|
|
|
|
function loadESXPlayer(identifier, playerId, isNew)
|
|
local userData = {
|
|
accounts = {},
|
|
inventory = {},
|
|
loadout = {},
|
|
weight = 0,
|
|
name = GetPlayerName(playerId),
|
|
identifier = identifier,
|
|
firstName = "John",
|
|
lastName = "Doe",
|
|
dateofbirth = "01/01/2000",
|
|
height = 120,
|
|
dead = false,
|
|
}
|
|
|
|
local result = MySQL.prepare.await(loadPlayer, { identifier })
|
|
|
|
-- Accounts
|
|
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
|
|
|
|
local index = #userData.accounts + 1
|
|
userData.accounts[index] = {
|
|
name = account,
|
|
money = accounts[account] or Config.StartingAccountMoney[account] or 0,
|
|
label = data.label,
|
|
round = data.round,
|
|
index = index,
|
|
}
|
|
end
|
|
|
|
-- Job
|
|
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))
|
|
job, grade = "unemployed", "0"
|
|
end
|
|
|
|
local jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
|
|
|
userData.job = {
|
|
id = jobObject.id,
|
|
name = jobObject.name,
|
|
label = jobObject.label,
|
|
|
|
grade = tonumber(grade),
|
|
grade_name = gradeObject.name,
|
|
grade_label = gradeObject.label,
|
|
grade_salary = gradeObject.salary,
|
|
|
|
skin_male = gradeObject.skin_male and json.decode(gradeObject.skin_male) or {},
|
|
skin_female = gradeObject.skin_female and json.decode(gradeObject.skin_female) or {},
|
|
}
|
|
|
|
-- Group
|
|
if result.group then
|
|
if result.group == "superadmin" then
|
|
userData.group = "admin"
|
|
print("[^3WARNING^7] ^5Superadmin^7 detected, setting group to ^5admin^7")
|
|
else
|
|
userData.group = result.group
|
|
end
|
|
else
|
|
userData.group = "user"
|
|
end
|
|
|
|
-- Loadout
|
|
if result.loadout and result.loadout ~= "" then
|
|
local loadout = json.decode(result.loadout)
|
|
for name, weapon in pairs(loadout) do
|
|
local label = ESX.GetWeaponLabel(name)
|
|
|
|
if label then
|
|
userData.loadout[#userData.loadout + 1] = {
|
|
name = name,
|
|
ammo = weapon.ammo,
|
|
label = label,
|
|
components = weapon.components or {},
|
|
tintIndex = weapon.tintIndex or 0,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Position
|
|
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 }
|
|
|
|
-- Metadata
|
|
userData.metadata = (result.metadata and result.metadata ~= "") and json.decode(result.metadata) or {}
|
|
|
|
-- xPlayer Creation
|
|
local xPlayer = CreateExtendedPlayer(
|
|
playerId,
|
|
identifier,
|
|
nil,
|
|
userData.group,
|
|
userData.accounts,
|
|
userData.inventory,
|
|
userData.weight,
|
|
userData.job,
|
|
userData.loadout,
|
|
GetPlayerName(playerId),
|
|
userData.coords,
|
|
userData.metadata
|
|
)
|
|
|
|
GlobalState["playerCount"] = GlobalState["playerCount"] + 1
|
|
ESX.Players[playerId] = xPlayer
|
|
Core.playersByIdentifier[identifier] = xPlayer
|
|
|
|
-- Identity
|
|
if result.firstname and result.firstname ~= "" then
|
|
userData.firstName = result.firstname
|
|
userData.lastName = result.lastname
|
|
|
|
local name = ("%s %s"):format(result.firstname, result.lastname)
|
|
userData.name = name
|
|
|
|
xPlayer.set("firstName", result.firstname)
|
|
xPlayer.set("lastName", result.lastname)
|
|
xPlayer.setName(name)
|
|
|
|
if result.dateofbirth then
|
|
userData.dateofbirth = result.dateofbirth
|
|
xPlayer.set("dateofbirth", result.dateofbirth)
|
|
end
|
|
if result.sex then
|
|
userData.sex = result.sex
|
|
xPlayer.set("sex", result.sex)
|
|
end
|
|
if result.height then
|
|
userData.height = result.height
|
|
xPlayer.set("height", result.height)
|
|
end
|
|
end
|
|
|
|
local inventory = exports['codem-inventory']:LoadInventory(playerId)
|
|
xPlayer.set('inv', inventory)
|
|
userData.inventory = inventory
|
|
|
|
TriggerEvent("esx:playerLoaded", playerId, xPlayer, isNew)
|
|
userData.money = xPlayer.getMoney()
|
|
userData.maxWeight = xPlayer.getMaxWeight()
|
|
userData.variables = xPlayer.variables or {}
|
|
xPlayer.triggerEvent("esx:playerLoaded", userData, isNew, userData.skin)
|
|
|
|
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
|
|
|
|
|
|
AddEventHandler("chatMessage", function(playerId, _, message)
|
|
local xPlayer = ESX.GetPlayerFromId(playerId)
|
|
if xPlayer and message:sub(1, 1) == "/" and playerId > 0 then
|
|
CancelEvent()
|
|
local commandName = message:sub(1):gmatch("%w+")()
|
|
xPlayer.showNotification(TranslateCap("commanderror_invalidcommand", commandName))
|
|
end
|
|
end)
|
|
|
|
---@param reason string
|
|
AddEventHandler("playerDropped", function(reason)
|
|
onPlayerDropped(source --[[@as number]], reason)
|
|
end)
|
|
|
|
AddEventHandler("esx:playerLoaded", function(_, xPlayer, isNew)
|
|
local job = xPlayer.getJob().name
|
|
local jobKey = ("%s:count"):format(job)
|
|
|
|
Core.JobsPlayerCount[job] = (Core.JobsPlayerCount[job] or 0) + 1
|
|
GlobalState[jobKey] = Core.JobsPlayerCount[job]
|
|
if isNew then
|
|
Player(xPlayer.source).state:set('isNew', true, false)
|
|
end
|
|
end)
|
|
|
|
AddEventHandler("esx:setJob", function(_, job, lastJob)
|
|
local lastJobKey = ("%s:count"):format(lastJob.name)
|
|
local jobKey = ("%s:count"):format(job.name)
|
|
local currentLastJob = Core.JobsPlayerCount[lastJob.name]
|
|
|
|
Core.JobsPlayerCount[lastJob.name] = ((currentLastJob and currentLastJob > 0) and currentLastJob or 1) - 1
|
|
Core.JobsPlayerCount[job.name] = (Core.JobsPlayerCount[job.name] or 0) + 1
|
|
|
|
GlobalState[lastJobKey] = Core.JobsPlayerCount[lastJob.name]
|
|
GlobalState[jobKey] = Core.JobsPlayerCount[job.name]
|
|
end)
|
|
|
|
AddEventHandler("esx:playerLogout", function(playerId, cb)
|
|
onPlayerDropped(playerId, "esx_player_logout", cb)
|
|
TriggerClientEvent("esx:onPlayerLogout", playerId)
|
|
end)
|
|
|
|
if not Config.CustomInventory then
|
|
RegisterNetEvent("esx:updateWeaponAmmo", function(weaponName, ammoCount)
|
|
local xPlayer = ESX.GetPlayerFromId(source)
|
|
|
|
if xPlayer then
|
|
xPlayer.updateWeaponAmmo(weaponName, ammoCount)
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent("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
|
|
print(("[^3WARNING^7] Player Detected Cheating: ^5%s^7"):format(GetPlayerName(playerId)))
|
|
return
|
|
end
|
|
|
|
if itemType == "item_standard" then
|
|
local sourceItem = sourceXPlayer.getInventoryItem(itemName)
|
|
|
|
if not sourceItem then
|
|
return
|
|
end
|
|
|
|
if itemCount < 1 or sourceItem.count < itemCount then
|
|
return sourceXPlayer.showNotification(TranslateCap("imp_invalid_quantity"))
|
|
end
|
|
|
|
if not targetXPlayer.canCarryItem(itemName, itemCount) then
|
|
return sourceXPlayer.showNotification(TranslateCap("ex_inv_lim", targetXPlayer.name))
|
|
end
|
|
|
|
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))
|
|
elseif itemType == "item_account" then
|
|
if itemCount < 1 or sourceXPlayer.getAccount(itemName).money < itemCount then
|
|
return sourceXPlayer.showNotification(TranslateCap("imp_invalid_amount"))
|
|
end
|
|
|
|
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))
|
|
elseif itemType == "item_weapon" then
|
|
if not sourceXPlayer.hasWeapon(itemName) then
|
|
return
|
|
end
|
|
|
|
local weaponLabel = ESX.GetWeaponLabel(itemName)
|
|
if targetXPlayer.hasWeapon(itemName) then
|
|
sourceXPlayer.showNotification(TranslateCap("gave_weapon_hasalready", targetXPlayer.name, weaponLabel))
|
|
targetXPlayer.showNotification(TranslateCap("received_weapon_hasalready", sourceXPlayer.name, weaponLabel))
|
|
return
|
|
end
|
|
|
|
local _, weapon = sourceXPlayer.getWeapon(itemName)
|
|
if not weapon then
|
|
return
|
|
end
|
|
|
|
local _, weaponObject = ESX.GetWeapon(itemName)
|
|
itemCount = weapon.ammo
|
|
local weaponComponents = ESX.Table.Clone(weapon.components)
|
|
local weaponTint = weapon.tintIndex
|
|
|
|
if weaponTint then
|
|
targetXPlayer.setWeaponTint(itemName, weaponTint)
|
|
end
|
|
|
|
if weaponComponents then
|
|
for _, v in pairs(weaponComponents) do
|
|
targetXPlayer.addWeaponComponent(itemName, v)
|
|
end
|
|
end
|
|
|
|
sourceXPlayer.removeWeapon(itemName)
|
|
targetXPlayer.addWeapon(itemName, itemCount)
|
|
|
|
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))
|
|
else
|
|
sourceXPlayer.showNotification(TranslateCap("gave_weapon", weaponLabel, targetXPlayer.name))
|
|
targetXPlayer.showNotification(TranslateCap("received_weapon", weaponLabel, sourceXPlayer.name))
|
|
end
|
|
elseif itemType == "item_ammo" then
|
|
if not sourceXPlayer.hasWeapon(itemName) then
|
|
return
|
|
end
|
|
|
|
local _, weapon = sourceXPlayer.getWeapon(itemName)
|
|
if not weapon then
|
|
return
|
|
end
|
|
|
|
if not targetXPlayer.hasWeapon(itemName) then
|
|
sourceXPlayer.showNotification(TranslateCap("gave_weapon_noweapon", targetXPlayer.name))
|
|
targetXPlayer.showNotification(TranslateCap("received_weapon_noweapon", sourceXPlayer.name, weapon.label))
|
|
return
|
|
end
|
|
|
|
local _, weaponObject = ESX.GetWeapon(itemName)
|
|
|
|
if not weaponObject.ammo then return end
|
|
|
|
local ammoLabel = weaponObject.ammo.label
|
|
if weapon.ammo >= itemCount 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))
|
|
end
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent("esx:removeInventoryItem", function(itemType, itemName, itemCount)
|
|
local playerId = source
|
|
local xPlayer = ESX.GetPlayerFromId(playerId)
|
|
|
|
if not xPlayer then
|
|
return
|
|
end
|
|
|
|
if itemType == "item_standard" then
|
|
if not itemCount or itemCount < 1 then
|
|
return xPlayer.showNotification(TranslateCap("imp_invalid_quantity"))
|
|
end
|
|
|
|
local xItem = xPlayer.getInventoryItem(itemName)
|
|
if not xItem then
|
|
return
|
|
end
|
|
|
|
if itemCount > xItem.count or xItem.count < 1 then
|
|
return xPlayer.showNotification(TranslateCap("imp_invalid_quantity"))
|
|
end
|
|
|
|
xPlayer.removeInventoryItem(itemName, itemCount)
|
|
local pickupLabel = ("%s [%s]"):format(xItem.label, itemCount)
|
|
ESX.CreatePickup("item_standard", itemName, itemCount, pickupLabel, playerId)
|
|
xPlayer.showNotification(TranslateCap("threw_standard", itemCount, xItem.label))
|
|
elseif itemType == "item_account" then
|
|
if itemCount == nil or itemCount < 1 then
|
|
return xPlayer.showNotification(TranslateCap("imp_invalid_amount"))
|
|
end
|
|
|
|
local account = xPlayer.getAccount(itemName)
|
|
if not account then
|
|
return
|
|
end
|
|
|
|
if itemCount > account.money or account.money < 1 then
|
|
return xPlayer.showNotification(TranslateCap("imp_invalid_amount"))
|
|
end
|
|
|
|
xPlayer.removeAccountMoney(itemName, itemCount, "Threw away")
|
|
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)))
|
|
elseif itemType == "item_weapon" then
|
|
itemName = string.upper(itemName)
|
|
|
|
if not xPlayer.hasWeapon(itemName) then return end
|
|
|
|
local _, weapon = xPlayer.getWeapon(itemName)
|
|
if not weapon then
|
|
return
|
|
end
|
|
|
|
local _, weaponObject = ESX.GetWeapon(itemName)
|
|
-- 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
|
|
weaponPickupLabel = ("%s [%s %s]"):format(weapon.label, weapon.ammo, ammoLabel)
|
|
xPlayer.showNotification(TranslateCap("threw_weapon_ammo", weapon.label, weapon.ammo, ammoLabel))
|
|
else
|
|
weaponPickupLabel = ("%s"):format(weapon.label)
|
|
xPlayer.showNotification(TranslateCap("threw_weapon", weapon.label))
|
|
end
|
|
|
|
ESX.CreatePickup("item_weapon", itemName, weapon.ammo, weaponPickupLabel, playerId, components, weapon.tintIndex)
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent("esx:useItem", function(itemName)
|
|
local source = source
|
|
local xPlayer = ESX.GetPlayerFromId(source)
|
|
|
|
if not xPlayer then
|
|
return
|
|
end
|
|
|
|
local count = xPlayer.getInventoryItem(itemName).count
|
|
|
|
if count < 1 then
|
|
return xPlayer.showNotification(TranslateCap("act_imp"))
|
|
end
|
|
|
|
ESX.UseItem(source, itemName)
|
|
end)
|
|
|
|
RegisterNetEvent("esx:onPickup", function(pickupId)
|
|
local pickup, xPlayer, success = Core.Pickups[pickupId], ESX.GetPlayerFromId(source)
|
|
|
|
if not xPlayer then
|
|
return
|
|
end
|
|
|
|
if not pickup then return end
|
|
|
|
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
|
|
|
|
if pickup.type == "item_standard" then
|
|
if not xPlayer.canCarryItem(pickup.name, pickup.count) then
|
|
return xPlayer.showNotification(TranslateCap("threw_cannot_pickup"))
|
|
end
|
|
|
|
xPlayer.addInventoryItem(pickup.name, pickup.count)
|
|
success = true
|
|
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
|
|
return xPlayer.showNotification(TranslateCap("threw_weapon_already"))
|
|
end
|
|
|
|
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)
|
|
end
|
|
end
|
|
|
|
if success then
|
|
Core.Pickups[pickupId] = nil
|
|
TriggerClientEvent("esx:removePickup", -1, pickupId)
|
|
end
|
|
end)
|
|
end
|
|
|
|
ESX.RegisterServerCallback("esx:getPlayerData", function(source, cb)
|
|
local xPlayer = ESX.GetPlayerFromId(source)
|
|
|
|
if not xPlayer then
|
|
return
|
|
end
|
|
|
|
cb({
|
|
identifier = xPlayer.identifier,
|
|
accounts = xPlayer.getAccounts(),
|
|
inventory = xPlayer.getInventory(),
|
|
job = xPlayer.getJob(),
|
|
loadout = xPlayer.getLoadout(),
|
|
money = xPlayer.getMoney(),
|
|
position = xPlayer.getCoords(true),
|
|
metadata = xPlayer.getMeta(),
|
|
})
|
|
end)
|
|
|
|
ESX.RegisterServerCallback("esx:isUserAdmin", function(source, cb)
|
|
cb(Core.IsPlayerAdmin(source))
|
|
end)
|
|
|
|
ESX.RegisterServerCallback("esx:getGameBuild", function(_, cb)
|
|
cb(tonumber(GetConvar("sv_enforceGameBuild", "1604")))
|
|
end)
|
|
|
|
ESX.RegisterServerCallback("esx:getOtherPlayerData", function(_, cb, target)
|
|
local xPlayer = ESX.GetPlayerFromId(target)
|
|
|
|
if not xPlayer then
|
|
return
|
|
end
|
|
|
|
cb({
|
|
identifier = xPlayer.identifier,
|
|
accounts = xPlayer.getAccounts(),
|
|
inventory = xPlayer.getInventory(),
|
|
job = xPlayer.getJob(),
|
|
loadout = xPlayer.getLoadout(),
|
|
money = xPlayer.getMoney(),
|
|
position = xPlayer.getCoords(true),
|
|
metadata = xPlayer.getMeta(),
|
|
})
|
|
end)
|
|
|
|
ESX.RegisterServerCallback("esx:getPlayerNames", function(source, cb, players)
|
|
players[source] = nil
|
|
|
|
for playerId, _ in pairs(players) do
|
|
local xPlayer = ESX.GetPlayerFromId(playerId)
|
|
|
|
if xPlayer then
|
|
players[playerId] = xPlayer.getName()
|
|
else
|
|
players[playerId] = nil
|
|
end
|
|
end
|
|
|
|
cb(players)
|
|
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)
|
|
if vehData.warp then
|
|
local vehicle = NetworkGetEntityFromNetworkId(id)
|
|
local timeout = 0
|
|
while GetVehiclePedIsIn(ped, false) ~= vehicle and timeout <= 15 do
|
|
Wait(0)
|
|
TaskWarpPedIntoVehicle(ped, vehicle, -1)
|
|
timeout += 1
|
|
end
|
|
end
|
|
cb(id)
|
|
end)
|
|
end)
|
|
|
|
AddEventHandler("txAdmin:events:scheduledRestart", function(eventData)
|
|
if eventData.secondsRemaining == 60 then
|
|
CreateThread(function()
|
|
Wait(50000)
|
|
Core.SavePlayers()
|
|
end)
|
|
end
|
|
end)
|
|
|
|
AddEventHandler("txAdmin:events:serverShuttingDown", function()
|
|
Core.SavePlayers()
|
|
end)
|
|
|
|
local DoNotUse = {
|
|
["essentialmode"] = true,
|
|
["es_admin2"] = true,
|
|
["basic-gamemode"] = true,
|
|
["mapmanager"] = true,
|
|
["fivem-map-skater"] = true,
|
|
["fivem-map-hipster"] = true,
|
|
["qb-core"] = true,
|
|
["default_spawnpoint"] = true,
|
|
}
|
|
|
|
AddEventHandler("onResourceStart", function(key)
|
|
if DoNotUse[string.lower(key)] then
|
|
while GetResourceState(key) ~= "started" do
|
|
Wait(0)
|
|
end
|
|
|
|
StopResource(key)
|
|
error(("WE STOPPED A RESOURCE THAT WILL BREAK ^1ESX^1, PLEASE REMOVE ^5%s^1"):format(key))
|
|
end
|
|
-- luacheck: ignore
|
|
if not SetEntityOrphanMode then
|
|
CreateThread(function()
|
|
while true do
|
|
error("ESX Requires a minimum Artifact version of 10188, Please update your server.")
|
|
Wait(60 * 1000)
|
|
end
|
|
end)
|
|
end
|
|
end)
|
|
|
|
for key in pairs(DoNotUse) do
|
|
if GetResourceState(key) == "started" or GetResourceState(key) == "starting" then
|
|
StopResource(key)
|
|
error(("WE STOPPED A RESOURCE THAT WILL BREAK ^1ESX^1, PLEASE REMOVE ^5%s^1"):format(key))
|
|
end
|
|
end
|