mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-29 01:08:54 +00:00
@@ -0,0 +1,3 @@
|
||||
[submodule "[esx_addons]/esx_multicharacter"]
|
||||
path = [esx_addons]/esx_multicharacter
|
||||
url = https://github.com/thelindat/esx_multicharacter.git
|
||||
@@ -21,7 +21,6 @@ Many more resources are included in this repository, or you can browse the [ESX
|
||||
|
||||
#### Optimisation
|
||||
- Utilise compile-time jenkins hashing over the GetHashKey native
|
||||
- Update old MySQL queries to use MySQL.store to improve performance, especially during player saving
|
||||
- Several loops will now sleep when their tasks are not necessary to perform
|
||||
- Improved support when using ESX Identity to reduce events and queries during player login
|
||||
- Support for the latest weapons and components
|
||||
|
||||
@@ -2,6 +2,6 @@ AddEventHandler('esx:getSharedObject', function(cb)
|
||||
cb(ESX)
|
||||
end)
|
||||
|
||||
function getSharedObject()
|
||||
exports('getSharedObject', function()
|
||||
return ESX
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
ESX = {}
|
||||
Core = {}
|
||||
ESX.PlayerData = {}
|
||||
ESX.PlayerLoaded = false
|
||||
ESX.CurrentRequestId = 0
|
||||
ESX.ServerCallbacks = {}
|
||||
ESX.TimeoutCallbacks = {}
|
||||
Core.CurrentRequestId = 0
|
||||
Core.ServerCallbacks = {}
|
||||
Core.TimeoutCallbacks = {}
|
||||
|
||||
ESX.UI = {}
|
||||
ESX.UI.HUD = {}
|
||||
@@ -21,15 +22,15 @@ ESX.Scaleform.Utils = {}
|
||||
ESX.Streaming = {}
|
||||
|
||||
ESX.SetTimeout = function(msec, cb)
|
||||
table.insert(ESX.TimeoutCallbacks, {
|
||||
table.insert(Core.TimeoutCallbacks, {
|
||||
time = GetGameTimer() + msec,
|
||||
cb = cb
|
||||
})
|
||||
return #ESX.TimeoutCallbacks
|
||||
return #Core.TimeoutCallbacks
|
||||
end
|
||||
|
||||
ESX.ClearTimeout = function(i)
|
||||
ESX.TimeoutCallbacks[i] = nil
|
||||
Core.TimeoutCallbacks[i] = nil
|
||||
end
|
||||
|
||||
ESX.IsPlayerLoaded = function()
|
||||
@@ -86,14 +87,14 @@ ESX.ShowFloatingHelpNotification = function(msg, coords)
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback = function(name, cb, ...)
|
||||
ESX.ServerCallbacks[ESX.CurrentRequestId] = cb
|
||||
Core.ServerCallbacks[Core.CurrentRequestId] = cb
|
||||
|
||||
TriggerServerEvent('esx:triggerServerCallback', name, ESX.CurrentRequestId, ...)
|
||||
TriggerServerEvent('esx:triggerServerCallback', name, Core.CurrentRequestId, ...)
|
||||
|
||||
if ESX.CurrentRequestId < 65535 then
|
||||
ESX.CurrentRequestId = ESX.CurrentRequestId + 1
|
||||
if Core.CurrentRequestId < 65535 then
|
||||
Core.CurrentRequestId = Core.CurrentRequestId + 1
|
||||
else
|
||||
ESX.CurrentRequestId = 0
|
||||
Core.CurrentRequestId = 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -849,7 +850,7 @@ ESX.ShowInventory = function()
|
||||
players[GetPlayerServerId(playerNearby)] = true
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback('esx:getPlayerNames', function(returnedPlayers)
|
||||
Core.TriggerServerCallback('esx:getPlayerNames', function(returnedPlayers)
|
||||
for playerId,playerName in pairs(returnedPlayers) do
|
||||
table.insert(elements, {
|
||||
label = playerName,
|
||||
@@ -989,8 +990,8 @@ end
|
||||
|
||||
RegisterNetEvent('esx:serverCallback')
|
||||
AddEventHandler('esx:serverCallback', function(requestId, ...)
|
||||
ESX.ServerCallbacks[requestId](...)
|
||||
ESX.ServerCallbacks[requestId] = nil
|
||||
Core.ServerCallbacks[requestId](...)
|
||||
Core.ServerCallbacks[requestId] = nil
|
||||
end)
|
||||
|
||||
RegisterNetEvent('esx:showNotification')
|
||||
@@ -1012,13 +1013,13 @@ end)
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
local sleep = 100
|
||||
if #ESX.TimeoutCallbacks > 0 then
|
||||
if #Core.TimeoutCallbacks > 0 then
|
||||
local currTime = GetGameTimer()
|
||||
sleep = 0
|
||||
for i=1, #ESX.TimeoutCallbacks, 1 do
|
||||
if currTime >= ESX.TimeoutCallbacks[i].time then
|
||||
ESX.TimeoutCallbacks[i].cb()
|
||||
ESX.TimeoutCallbacks[i] = nil
|
||||
for i=1, #Core.TimeoutCallbacks, 1 do
|
||||
if currTime >= Core.TimeoutCallbacks[i].time then
|
||||
Core.TimeoutCallbacks[i].cb()
|
||||
Core.TimeoutCallbacks[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,15 +32,14 @@ AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin)
|
||||
}, function()
|
||||
TriggerServerEvent('esx:onPlayerSpawn')
|
||||
TriggerEvent('esx:onPlayerSpawn')
|
||||
TriggerEvent('playerSpawned') -- compatibility with old scripts
|
||||
TriggerEvent('esx:restoreLoadout')
|
||||
|
||||
if isNew then
|
||||
if skin.sex == 0 then
|
||||
TriggerEvent('skinchanger:loadDefaultModel', true)
|
||||
else
|
||||
TriggerEvent('skinchanger:loadDefaultModel', false)
|
||||
end
|
||||
elseif skin then TriggerEvent('skinchanger:loadSkin', skin) end
|
||||
TriggerEvent('skinchanger:loadDefaultModel', skin.sex == 0)
|
||||
elseif skin then
|
||||
TriggerEvent('skinchanger:loadSkin', skin)
|
||||
end
|
||||
|
||||
TriggerEvent('esx:loadingScreenOff')
|
||||
ShutdownLoadingScreen()
|
||||
ShutdownLoadingScreenNui()
|
||||
@@ -65,7 +64,7 @@ AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin)
|
||||
|
||||
local gradeLabel = ESX.PlayerData.job.grade_label ~= ESX.PlayerData.job.label and ESX.PlayerData.job.grade_label or ''
|
||||
if gradeLabel ~= '' then gradeLabel = ' - '..gradeLabel end
|
||||
|
||||
|
||||
ESX.UI.HUD.RegisterElement('job', #ESX.PlayerData.accounts, 0, jobTpl, {
|
||||
job_label = ESX.PlayerData.job.label,
|
||||
grade_label = gradeLabel
|
||||
@@ -83,10 +82,15 @@ end)
|
||||
RegisterNetEvent('esx:setMaxWeight')
|
||||
AddEventHandler('esx:setMaxWeight', function(newMaxWeight) ESX.PlayerData.maxWeight = newMaxWeight end)
|
||||
|
||||
AddEventHandler('esx:onPlayerSpawn', function()
|
||||
ESX.SetPlayerData('ped', PlayerPedId())
|
||||
ESX.SetPlayerData('dead', false)
|
||||
end)
|
||||
local function onPlayerSpawn()
|
||||
if ESX.PlayerLoaded then
|
||||
ESX.SetPlayerData('ped', PlayerPedId())
|
||||
ESX.SetPlayerData('dead', false)
|
||||
end
|
||||
end
|
||||
|
||||
AddEventHandler('playerSpawned', onPlayerSpawn)
|
||||
AddEventHandler('esx:onPlayerSpawn', onPlayerSpawn)
|
||||
|
||||
AddEventHandler('esx:onPlayerDeath', function()
|
||||
ESX.SetPlayerData('ped', PlayerPedId())
|
||||
@@ -282,7 +286,7 @@ end)
|
||||
|
||||
RegisterNetEvent('esx:createMissingPickups')
|
||||
AddEventHandler('esx:createMissingPickups', function(missingPickups)
|
||||
for pickupId,pickup in pairs(missingPickups) do
|
||||
for pickupId, pickup in pairs(missingPickups) do
|
||||
TriggerEvent('esx:createPickup', pickupId, pickup.label, pickup.coords, pickup.type, pickup.name, pickup.components, pickup.tintIndex)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -15,7 +15,6 @@ shared_scripts {
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'@async/async.lua',
|
||||
'@mysql-async/lib/MySQL.lua',
|
||||
|
||||
'server/common.lua',
|
||||
@@ -69,16 +68,8 @@ files {
|
||||
'html/img/accounts/money.png'
|
||||
}
|
||||
|
||||
exports {
|
||||
'getSharedObject'
|
||||
}
|
||||
|
||||
server_exports {
|
||||
'getSharedObject'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
'mysql-async',
|
||||
'oxmysql',
|
||||
'async',
|
||||
'spawnmanager',
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory,
|
||||
self.variables = {}
|
||||
self.weight = weight
|
||||
self.maxWeight = Config.MaxWeight
|
||||
if Config.Multichar then self.license = 'license'..string.sub(identifier, 6) else self.license = 'license:'..identifier end
|
||||
if Config.Multichar then self.license = 'license'.. identifier:sub(identifier:find(':'), identifier:len()) else self.license = 'license:'..identifier end
|
||||
|
||||
ExecuteCommand(('add_principal identifier.%s group.%s'):format(self.license, self.group))
|
||||
|
||||
|
||||
@@ -134,13 +134,13 @@ end, true, {help = _U('command_setgroup'), validate = true, arguments = {
|
||||
}})
|
||||
|
||||
ESX.RegisterCommand('save', 'admin', function(xPlayer, args, showError)
|
||||
ESX.SavePlayer(args.playerId)
|
||||
Core.SavePlayer(args.playerId)
|
||||
end, true, {help = _U('command_save'), validate = true, arguments = {
|
||||
{name = 'playerId', help = _U('commandgeneric_playerid'), type = 'player'}
|
||||
}})
|
||||
|
||||
ESX.RegisterCommand('saveall', 'admin', function(xPlayer, args, showError)
|
||||
ESX.SavePlayers()
|
||||
Core.SavePlayers()
|
||||
end, true, {help = _U('command_saveall')})
|
||||
|
||||
ESX.RegisterCommand('group', {"user", "admin"}, function(xPlayer, args, showError)
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
ESX = {}
|
||||
ESX.Players = {}
|
||||
ESX.UsableItemsCallbacks = {}
|
||||
ESX.Items = {}
|
||||
ESX.ServerCallbacks = {}
|
||||
ESX.TimeoutCount = -1
|
||||
ESX.CancelledTimeouts = {}
|
||||
ESX.Pickups = {}
|
||||
ESX.PickupId = 0
|
||||
ESX.Jobs = {}
|
||||
ESX.RegisteredCommands = {}
|
||||
ESX.Items = {}
|
||||
Core = {}
|
||||
Core.UsableItemsCallbacks = {}
|
||||
Core.ServerCallbacks = {}
|
||||
Core.TimeoutCount = -1
|
||||
Core.CancelledTimeouts = {}
|
||||
Core.RegisteredCommands = {}
|
||||
Core.Pickups = {}
|
||||
Core.PickupId = 0
|
||||
|
||||
AddEventHandler('esx:getSharedObject', function(cb)
|
||||
cb(ESX)
|
||||
end)
|
||||
|
||||
function getSharedObject()
|
||||
exports('getSharedObject', function()
|
||||
return ESX
|
||||
end
|
||||
end)
|
||||
|
||||
local function StartDBSync()
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(10 * 60 * 1000)
|
||||
ESX.SavePlayers()
|
||||
Core.SavePlayers()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
MySQL.ready(function()
|
||||
MySQL.Async.fetchAll('SELECT * FROM items', {}, function(result)
|
||||
MySQL.query('SELECT * FROM items', {}, function(result)
|
||||
for k,v in ipairs(result) do
|
||||
ESX.Items[v.name] = {
|
||||
label = v.label,
|
||||
@@ -40,13 +41,13 @@ MySQL.ready(function()
|
||||
end)
|
||||
|
||||
local Jobs = {}
|
||||
MySQL.Async.fetchAll('SELECT * FROM jobs', {}, function(jobs)
|
||||
MySQL.query('SELECT * FROM jobs', {}, function(jobs)
|
||||
for k,v in ipairs(jobs) do
|
||||
Jobs[v.name] = v
|
||||
Jobs[v.name].grades = {}
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM job_grades', {}, function(jobGrades)
|
||||
MySQL.query('SELECT * FROM job_grades', {}, function(jobGrades)
|
||||
for k,v in ipairs(jobGrades) do
|
||||
if Jobs[v.job_name] then
|
||||
Jobs[v.job_name].grades[tostring(v.grade)] = v
|
||||
@@ -80,7 +81,7 @@ RegisterServerEvent('esx:triggerServerCallback')
|
||||
AddEventHandler('esx:triggerServerCallback', function(name, requestId, ...)
|
||||
local playerId = source
|
||||
|
||||
ESX.TriggerServerCallback(name, requestId, playerId, function(...)
|
||||
Core.TriggerServerCallback(name, requestId, playerId, function(...)
|
||||
TriggerClientEvent('esx:serverCallback', playerId, requestId, ...)
|
||||
end, ...)
|
||||
end)
|
||||
|
||||
@@ -5,17 +5,17 @@ ESX.Trace = function(msg)
|
||||
end
|
||||
|
||||
ESX.SetTimeout = function(msec, cb)
|
||||
local id = ESX.TimeoutCount + 1
|
||||
local id = Core.TimeoutCount + 1
|
||||
|
||||
SetTimeout(msec, function()
|
||||
if ESX.CancelledTimeouts[id] then
|
||||
ESX.CancelledTimeouts[id] = nil
|
||||
if Core.CancelledTimeouts[id] then
|
||||
Core.CancelledTimeouts[id] = nil
|
||||
else
|
||||
cb()
|
||||
end
|
||||
end)
|
||||
|
||||
ESX.TimeoutCount = id
|
||||
Core.TimeoutCount = id
|
||||
|
||||
return id
|
||||
end
|
||||
@@ -29,10 +29,10 @@ ESX.RegisterCommand = function(name, group, cb, allowConsole, suggestion)
|
||||
return
|
||||
end
|
||||
|
||||
if ESX.RegisteredCommands[name] then
|
||||
if Core.RegisteredCommands[name] then
|
||||
print(('[^3WARNING^7] Command ^5"%s" already registered, overriding command'):format(name))
|
||||
|
||||
if ESX.RegisteredCommands[name].suggestion then
|
||||
if Core.RegisteredCommands[name].suggestion then
|
||||
TriggerClientEvent('chat:removeSuggestion', -1, ('/%s'):format(name))
|
||||
end
|
||||
end
|
||||
@@ -44,10 +44,10 @@ ESX.RegisterCommand = function(name, group, cb, allowConsole, suggestion)
|
||||
TriggerClientEvent('chat:addSuggestion', -1, ('/%s'):format(name), suggestion.help, suggestion.arguments)
|
||||
end
|
||||
|
||||
ESX.RegisteredCommands[name] = {group = group, cb = cb, allowConsole = allowConsole, suggestion = suggestion}
|
||||
Core.RegisteredCommands[name] = {group = group, cb = cb, allowConsole = allowConsole, suggestion = suggestion}
|
||||
|
||||
RegisterCommand(name, function(playerId, args, rawCommand)
|
||||
local command = ESX.RegisteredCommands[name]
|
||||
local command = Core.RegisteredCommands[name]
|
||||
|
||||
if not command.allowConsole and playerId == 0 then
|
||||
print(('[^3WARNING^7] ^5%s'):format(_U('commanderror_console')))
|
||||
@@ -148,92 +148,62 @@ ESX.RegisterCommand = function(name, group, cb, allowConsole, suggestion)
|
||||
end
|
||||
|
||||
ESX.ClearTimeout = function(id)
|
||||
ESX.CancelledTimeouts[id] = true
|
||||
Core.CancelledTimeouts[id] = true
|
||||
end
|
||||
|
||||
ESX.RegisterServerCallback = function(name, cb)
|
||||
ESX.ServerCallbacks[name] = cb
|
||||
Core.ServerCallbacks[name] = cb
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback = function(name, requestId, source, cb, ...)
|
||||
if ESX.ServerCallbacks[name] then
|
||||
ESX.ServerCallbacks[name](source, cb, ...)
|
||||
Core.TriggerServerCallback = function(name, requestId, source, cb, ...)
|
||||
if Core.ServerCallbacks[name] then
|
||||
Core.ServerCallbacks[name](source, cb, ...)
|
||||
else
|
||||
print(('[^3WARNING^7] Server callback ^5"%s"^0 does not exist. ^1Please Check The Server File for Errors!'):format(name))
|
||||
end
|
||||
end
|
||||
|
||||
local savePlayers = -1
|
||||
Citizen.CreateThread(function()
|
||||
savePlayers = MySQL.Sync.store("UPDATE users SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ?, `loadout` = ? WHERE `identifier` = ?")
|
||||
end)
|
||||
|
||||
ESX.SavePlayer = function(xPlayer, cb)
|
||||
local asyncTasks = {}
|
||||
|
||||
table.insert(asyncTasks, function(cb2)
|
||||
MySQL.Async.execute(savePlayers, {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
xPlayer.job.name,
|
||||
xPlayer.job.grade,
|
||||
xPlayer.getGroup(),
|
||||
json.encode(xPlayer.getCoords()),
|
||||
json.encode(xPlayer.getInventory(true)),
|
||||
json.encode(xPlayer.getLoadout(true)),
|
||||
xPlayer.getIdentifier()
|
||||
}, function(rowsChanged)
|
||||
cb2()
|
||||
end)
|
||||
end)
|
||||
|
||||
Async.parallel(asyncTasks, function(results)
|
||||
print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.getName()))
|
||||
|
||||
if cb then
|
||||
cb()
|
||||
Core.SavePlayer = function(xPlayer, cb)
|
||||
MySQL.prepare('UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ?, `loadout` = ? WHERE `identifier` = ?', {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
xPlayer.job.name,
|
||||
xPlayer.job.grade,
|
||||
xPlayer.group,
|
||||
json.encode(xPlayer.getCoords()),
|
||||
json.encode(xPlayer.getInventory(true)),
|
||||
json.encode(xPlayer.getLoadout(true)),
|
||||
xPlayer.identifier
|
||||
}, function(affectedRows)
|
||||
if affectedRows == 1 then
|
||||
print(('[^2INFO^7] Saved player ^5"%s^7"'):format(xPlayer.name))
|
||||
end
|
||||
if cb then cb() end
|
||||
end)
|
||||
end
|
||||
|
||||
ESX.SavePlayers = function(cb)
|
||||
Core.SavePlayers = function(cb)
|
||||
local xPlayers = ESX.GetExtendedPlayers()
|
||||
if #xPlayers > 0 then
|
||||
local count = #xPlayers
|
||||
if count > 0 then
|
||||
local parameters = {}
|
||||
local time = os.time()
|
||||
|
||||
local selectListWithNames = "SELECT '%s' AS identifier, '%s' AS new_accounts, '%s' AS new_job, %s AS new_job_grade, '%s' AS new_group, '%s' AS new_loadout, '%s' AS new_position, '%s' AS new_inventory "
|
||||
local selectListNoNames = "SELECT '%s', '%s', '%s' , %s, '%s', '%s', '%s', '%s' "
|
||||
|
||||
local updateCommand = 'UPDATE users u JOIN ('
|
||||
|
||||
local selectList = selectListNoNames
|
||||
local first = true
|
||||
for k, xPlayer in pairs(xPlayers) do
|
||||
if first == false then
|
||||
updateCommand = updateCommand .. ' UNION '
|
||||
else
|
||||
selectList = selectListWithNames
|
||||
end
|
||||
|
||||
updateCommand = updateCommand .. string.format(selectList,
|
||||
xPlayer.identifier,
|
||||
for i=1, count do
|
||||
local xPlayer = xPlayers[i]
|
||||
parameters[#parameters+1] = {
|
||||
json.encode(xPlayer.getAccounts(true)),
|
||||
xPlayer.job.name,
|
||||
xPlayer.job.grade,
|
||||
xPlayer.getGroup(),
|
||||
json.encode(xPlayer.getLoadout(true)),
|
||||
xPlayer.group,
|
||||
json.encode(xPlayer.getCoords()),
|
||||
json.encode(xPlayer.getInventory(true))
|
||||
)
|
||||
|
||||
first = false
|
||||
json.encode(xPlayer.getInventory(true)),
|
||||
json.encode(xPlayer.getLoadout(true)),
|
||||
xPlayer.identifier
|
||||
}
|
||||
end
|
||||
|
||||
updateCommand = updateCommand .. ' ) vals ON u.identifier = vals.identifier SET accounts = new_accounts, job = new_job, job_grade = new_job_grade, `group` = new_group, loadout = new_loadout, `position` = new_position, inventory = new_inventory'
|
||||
|
||||
MySQL.Async.fetchAll(updateCommand, {},
|
||||
function(result)
|
||||
if result then
|
||||
if cb then cb() else print(('[^2INFO^7] Saved %s of %s player(s) over %s seconds'):format(result.affectedRows, #xPlayers, os.time() - time)) end
|
||||
MySQL.prepare("UPDATE `users` SET `accounts` = ?, `job` = ?, `job_grade` = ?, `group` = ?, `position` = ?, `inventory` = ?, `loadout` = ? WHERE `identifier` = ?", parameters,
|
||||
function(results)
|
||||
if results then
|
||||
if type(cb) == 'function' then cb() else print(('[^2INFO^7] Saved %s %s over %s ms'):format(count, count > 1 and 'players' or 'player', (os.time() - time) / 1000000)) end
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -285,11 +255,11 @@ ESX.GetIdentifier = function(playerId)
|
||||
end
|
||||
|
||||
ESX.RegisterUsableItem = function(item, cb)
|
||||
ESX.UsableItemsCallbacks[item] = cb
|
||||
Core.UsableItemsCallbacks[item] = cb
|
||||
end
|
||||
|
||||
ESX.UseItem = function(source, item)
|
||||
ESX.UsableItemsCallbacks[item](source, item)
|
||||
Core.UsableItemsCallbacks[item](source, item)
|
||||
end
|
||||
|
||||
ESX.GetItemLabel = function(item)
|
||||
@@ -298,24 +268,36 @@ ESX.GetItemLabel = function(item)
|
||||
end
|
||||
end
|
||||
|
||||
ESX.GetJobs = function()
|
||||
return ESX.Jobs
|
||||
end
|
||||
|
||||
ESX.GetUsableItems = function()
|
||||
local Usables = {}
|
||||
for k in pairs(Core.UsableItemsCallbacks) do
|
||||
Usables[k] = true
|
||||
end
|
||||
return Usables
|
||||
end
|
||||
|
||||
ESX.CreatePickup = function(type, name, count, label, playerId, components, tintIndex)
|
||||
local pickupId = (ESX.PickupId == 65635 and 0 or ESX.PickupId + 1)
|
||||
local pickupId = (Core.PickupId == 65635 and 0 or Core.PickupId + 1)
|
||||
local xPlayer = ESX.GetPlayerFromId(playerId)
|
||||
local coords = xPlayer.getCoords()
|
||||
|
||||
ESX.Pickups[pickupId] = {
|
||||
Core.Pickups[pickupId] = {
|
||||
type = type, name = name,
|
||||
count = count, label = label,
|
||||
coords = coords
|
||||
}
|
||||
|
||||
if type == 'item_weapon' then
|
||||
ESX.Pickups[pickupId].components = components
|
||||
ESX.Pickups[pickupId].tintIndex = tintIndex
|
||||
Core.Pickups[pickupId].components = components
|
||||
Core.Pickups[pickupId].tintIndex = tintIndex
|
||||
end
|
||||
|
||||
TriggerClientEvent('esx:createPickup', -1, pickupId, label, coords, type, name, components, tintIndex)
|
||||
ESX.PickupId = pickupId
|
||||
Core.PickupId = pickupId
|
||||
end
|
||||
|
||||
ESX.DoesJobExist = function(job, grade)
|
||||
@@ -329,3 +311,18 @@ ESX.DoesJobExist = function(job, grade)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
Core.IsPlayerAdmin = function(playerId)
|
||||
if (IsPlayerAceAllowed(playerId, 'command') or GetConvar('sv_lan', '') == 'true') and true or false then
|
||||
return true
|
||||
end
|
||||
|
||||
local xPlayer = ESX.GetPlayerFromId(playerId)
|
||||
if xPlayer then
|
||||
if xPlayer.group == 'admin' or xPlayer.group == 'superadmin' then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
+207
-230
@@ -1,27 +1,18 @@
|
||||
local NewPlayer, LoadPlayer = -1, -1
|
||||
Citizen.CreateThread(function()
|
||||
SetMapName('San Andreas')
|
||||
SetGameType('ESX Legacy')
|
||||
SetMapName('San Andreas')
|
||||
SetGameType('ESX Legacy')
|
||||
|
||||
local query = '`accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`' -- Select these fields from the database
|
||||
if Config.Multichar or Config.Identity then -- append these fields to the select query
|
||||
query = query..', `firstname`, `lastname`, `dateofbirth`, `sex`, `height`'
|
||||
end
|
||||
local newPlayer = 'INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?'
|
||||
local loadPlayer = 'SELECT `accounts`, `job`, `job_grade`, `group`, `position`, `inventory`, `skin`, `loadout`'
|
||||
|
||||
if Config.Multichar then -- insert identity data with creation
|
||||
MySQL.Async.store("INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?, `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?", function(storeId)
|
||||
NewPlayer = storeId
|
||||
end)
|
||||
else
|
||||
MySQL.Async.store("INSERT INTO `users` SET `accounts` = ?, `identifier` = ?, `group` = ?", function(storeId)
|
||||
NewPlayer = storeId
|
||||
end)
|
||||
end
|
||||
if Config.Multichar then
|
||||
newPlayer = newPlayer..', `firstname` = ?, `lastname` = ?, `dateofbirth` = ?, `sex` = ?, `height` = ?"'
|
||||
end
|
||||
|
||||
MySQL.Async.store("SELECT "..query.." FROM `users` WHERE identifier = ?", function(storeId)
|
||||
LoadPlayer = storeId
|
||||
end)
|
||||
end)
|
||||
if Config.Multichar or Config.Identity then
|
||||
loadPlayer = loadPlayer..', `firstname`, `lastname`, `dateofbirth`, `sex`, `height`'
|
||||
end
|
||||
|
||||
loadPlayer = loadPlayer..' FROM `users` WHERE identifier = ?'
|
||||
|
||||
if Config.Multichar then
|
||||
AddEventHandler('esx:onPlayerJoined', function(src, char, data)
|
||||
@@ -49,13 +40,12 @@ function onPlayerJoined(playerId)
|
||||
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
|
||||
MySQL.Async.fetchScalar('SELECT 1 FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
}, function(result)
|
||||
if result then
|
||||
loadESXPlayer(identifier, playerId, false)
|
||||
else createESXPlayer(identifier, playerId) end
|
||||
end)
|
||||
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.')
|
||||
@@ -69,7 +59,7 @@ function createESXPlayer(identifier, playerId, data)
|
||||
accounts[account] = money
|
||||
end
|
||||
|
||||
if IsPlayerAceAllowed(playerId, "command") then
|
||||
if Core.IsPlayerAdmin(playerId) then
|
||||
print(('^2[INFO] ^0 Player ^5%s ^0Has been granted admin permissions via ^5Ace Perms.^7'):format(playerId))
|
||||
defaultGroup = "admin"
|
||||
else
|
||||
@@ -77,49 +67,44 @@ function createESXPlayer(identifier, playerId, data)
|
||||
end
|
||||
|
||||
if not Config.Multichar then
|
||||
MySQL.Async.execute(NewPlayer, {
|
||||
json.encode(accounts),
|
||||
identifier,
|
||||
defaultGroup,
|
||||
}, function(rowsChanged)
|
||||
MySQL.prepare(newPlayer, { json.encode(accounts), identifier, defaultGroup }, function()
|
||||
loadESXPlayer(identifier, playerId, true)
|
||||
end)
|
||||
else
|
||||
MySQL.Async.execute(NewPlayer, {
|
||||
json.encode(accounts),
|
||||
identifier,
|
||||
defaultGroup,
|
||||
data.firstname,
|
||||
data.lastname,
|
||||
data.dateofbirth,
|
||||
data.sex,
|
||||
data.height,
|
||||
}, function(rowsChanged)
|
||||
MySQL.prepare(newPlayer, {
|
||||
json.encode(accounts),
|
||||
identifier,
|
||||
defaultGroup,
|
||||
data.firstname,
|
||||
data.lastname,
|
||||
data.dateofbirth,
|
||||
data.sex,
|
||||
data.height
|
||||
}, function()
|
||||
loadESXPlayer(identifier, playerId, true)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
AddEventHandler('playerConnecting', function(name, setCallback, deferrals)
|
||||
deferrals.defer()
|
||||
local playerId = source
|
||||
local identifier = ESX.GetIdentifier(playerId)
|
||||
Citizen.Wait(100)
|
||||
if not Config.Multichar then
|
||||
AddEventHandler('playerConnecting', function(name, setCallback, deferrals)
|
||||
deferrals.defer()
|
||||
local playerId = source
|
||||
local identifier = ESX.GetIdentifier(playerId)
|
||||
|
||||
if identifier then
|
||||
if ESX.GetPlayerFromIdentifier(identifier) then
|
||||
deferrals.done(('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))
|
||||
if identifier then
|
||||
if ESX.GetPlayerFromIdentifier(identifier) then
|
||||
deferrals.done(('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
|
||||
deferrals.done()
|
||||
end
|
||||
else
|
||||
deferrals.done()
|
||||
deferrals.done('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
|
||||
else
|
||||
deferrals.done('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)
|
||||
end
|
||||
|
||||
function loadESXPlayer(identifier, playerId, isNew)
|
||||
local tasks = {}
|
||||
|
||||
local userData = {
|
||||
accounts = {},
|
||||
inventory = {},
|
||||
@@ -129,179 +114,171 @@ function loadESXPlayer(identifier, playerId, isNew)
|
||||
weight = 0
|
||||
}
|
||||
|
||||
table.insert(tasks, function(cb)
|
||||
MySQL.Async.fetchAll(LoadPlayer, { identifier
|
||||
}, function(result)
|
||||
local job, grade, jobObject, gradeObject = result[1].job, tostring(result[1].job_grade)
|
||||
local foundAccounts, foundItems = {}, {}
|
||||
local result = MySQL.prepare.await(loadPlayer, { identifier })
|
||||
local job, grade, jobObject, gradeObject = result.job, tostring(result.job_grade)
|
||||
local foundAccounts, foundItems = {}, {}
|
||||
|
||||
-- Accounts
|
||||
if result[1].accounts and result[1].accounts ~= '' then
|
||||
local accounts = json.decode(result[1].accounts)
|
||||
-- Accounts
|
||||
if result.accounts and result.accounts ~= '' then
|
||||
local accounts = json.decode(result.accounts)
|
||||
|
||||
for account,money in pairs(accounts) do
|
||||
foundAccounts[account] = money
|
||||
end
|
||||
end
|
||||
|
||||
for account,label in pairs(Config.Accounts) do
|
||||
table.insert(userData.accounts, {
|
||||
name = account,
|
||||
money = foundAccounts[account] or Config.StartingAccountMoney[account] or 0,
|
||||
label = label
|
||||
})
|
||||
end
|
||||
|
||||
-- Job
|
||||
if ESX.DoesJobExist(job, grade) then
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid job for %s [job: %s, grade: %s]'):format(identifier, job, grade))
|
||||
job, grade = 'unemployed', '0'
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
end
|
||||
|
||||
userData.job.id = jobObject.id
|
||||
userData.job.name = jobObject.name
|
||||
userData.job.label = jobObject.label
|
||||
|
||||
userData.job.grade = tonumber(grade)
|
||||
userData.job.grade_name = gradeObject.name
|
||||
userData.job.grade_label = gradeObject.label
|
||||
userData.job.grade_salary = gradeObject.salary
|
||||
|
||||
userData.job.skin_male = {}
|
||||
userData.job.skin_female = {}
|
||||
|
||||
if gradeObject.skin_male then userData.job.skin_male = json.decode(gradeObject.skin_male) end
|
||||
if gradeObject.skin_female then userData.job.skin_female = json.decode(gradeObject.skin_female) end
|
||||
|
||||
-- Inventory
|
||||
if result[1].inventory and result[1].inventory ~= '' then
|
||||
local inventory = json.decode(result[1].inventory)
|
||||
|
||||
for name,count in pairs(inventory) do
|
||||
local item = ESX.Items[name]
|
||||
|
||||
if item then
|
||||
foundItems[name] = count
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(name, identifier))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for name,item in pairs(ESX.Items) do
|
||||
local count = foundItems[name] or 0
|
||||
if count > 0 then userData.weight = userData.weight + (item.weight * count) end
|
||||
|
||||
table.insert(userData.inventory, {
|
||||
name = name,
|
||||
count = count,
|
||||
label = item.label,
|
||||
weight = item.weight,
|
||||
usable = ESX.UsableItemsCallbacks[name] ~= nil,
|
||||
rare = item.rare,
|
||||
canRemove = item.canRemove
|
||||
})
|
||||
end
|
||||
|
||||
table.sort(userData.inventory, function(a, b)
|
||||
return a.label < b.label
|
||||
end)
|
||||
|
||||
-- Group
|
||||
if result[1].group then
|
||||
if result[1].group == "superadmin" then
|
||||
userData.group = "admin"
|
||||
else
|
||||
userData.group = result[1].group
|
||||
end
|
||||
else
|
||||
userData.group = 'user'
|
||||
end
|
||||
|
||||
-- Loadout
|
||||
if result[1].loadout and result[1].loadout ~= '' then
|
||||
local loadout = json.decode(result[1].loadout)
|
||||
|
||||
for name,weapon in pairs(loadout) do
|
||||
local label = ESX.GetWeaponLabel(name)
|
||||
|
||||
if label then
|
||||
if not weapon.components then weapon.components = {} end
|
||||
if not weapon.tintIndex then weapon.tintIndex = 0 end
|
||||
|
||||
table.insert(userData.loadout, {
|
||||
name = name,
|
||||
ammo = weapon.ammo,
|
||||
label = label,
|
||||
components = weapon.components,
|
||||
tintIndex = weapon.tintIndex
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Position
|
||||
if result[1].position and result[1].position ~= '' then
|
||||
userData.coords = json.decode(result[1].position)
|
||||
else
|
||||
print('[^3WARNING^7] Column ^5"position"^0 in ^5"users"^0 table is missing required default value. Using backup coords, fix your database.')
|
||||
userData.coords = {x = -269.4, y = -955.3, z = 31.2, heading = 205.8}
|
||||
end
|
||||
|
||||
-- Skin
|
||||
if result[1].skin and result[1].skin ~= '' then
|
||||
userData.skin = json.decode(result[1].skin)
|
||||
else
|
||||
if userData.sex == 'f' then userData.skin = {sex=1} else userData.skin = {sex=0} end
|
||||
end
|
||||
|
||||
-- Identity
|
||||
if result[1].firstname and result[1].firstname ~= '' then
|
||||
userData.firstname = result[1].firstname
|
||||
userData.lastname = result[1].lastname
|
||||
userData.playerName = userData.firstname..' '..userData.lastname
|
||||
if result[1].dateofbirth then userData.dateofbirth = result[1].dateofbirth end
|
||||
if result[1].sex then userData.sex = result[1].sex end
|
||||
if result[1].height then userData.height = result[1].height end
|
||||
end
|
||||
|
||||
cb()
|
||||
end)
|
||||
end)
|
||||
|
||||
Async.parallel(tasks, function(results)
|
||||
local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, userData.playerName, userData.coords)
|
||||
ESX.Players[playerId] = xPlayer
|
||||
|
||||
if userData.firstname then
|
||||
xPlayer.set('firstName', userData.firstname)
|
||||
xPlayer.set('lastName', userData.lastname)
|
||||
if userData.dateofbirth then xPlayer.set('dateofbirth', userData.dateofbirth) end
|
||||
if userData.sex then xPlayer.set('sex', userData.sex) end
|
||||
if userData.height then xPlayer.set('height', userData.height) end
|
||||
for account,money in pairs(accounts) do
|
||||
foundAccounts[account] = money
|
||||
end
|
||||
end
|
||||
|
||||
TriggerEvent('esx:playerLoaded', playerId, xPlayer, isNew)
|
||||
for account,label in pairs(Config.Accounts) do
|
||||
table.insert(userData.accounts, {
|
||||
name = account,
|
||||
money = foundAccounts[account] or Config.StartingAccountMoney[account] or 0,
|
||||
label = label
|
||||
})
|
||||
end
|
||||
|
||||
xPlayer.triggerEvent('esx:playerLoaded', {
|
||||
accounts = xPlayer.getAccounts(),
|
||||
coords = xPlayer.getCoords(),
|
||||
identifier = xPlayer.getIdentifier(),
|
||||
inventory = xPlayer.getInventory(),
|
||||
job = xPlayer.getJob(),
|
||||
loadout = xPlayer.getLoadout(),
|
||||
maxWeight = xPlayer.getMaxWeight(),
|
||||
money = xPlayer.getMoney(),
|
||||
dead = false
|
||||
}, isNew, userData.skin)
|
||||
-- Job
|
||||
if ESX.DoesJobExist(job, grade) then
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid job for %s [job: %s, grade: %s]'):format(identifier, job, grade))
|
||||
job, grade = 'unemployed', '0'
|
||||
jobObject, gradeObject = ESX.Jobs[job], ESX.Jobs[job].grades[grade]
|
||||
end
|
||||
|
||||
xPlayer.triggerEvent('esx:createMissingPickups', ESX.Pickups)
|
||||
xPlayer.triggerEvent('esx:registerSuggestions', ESX.RegisteredCommands)
|
||||
print(('[^2INFO^0] Player ^5"%s" ^0has connected to the server. ID: ^5%s^7'):format(xPlayer.getName(), playerId))
|
||||
userData.job.id = jobObject.id
|
||||
userData.job.name = jobObject.name
|
||||
userData.job.label = jobObject.label
|
||||
|
||||
userData.job.grade = tonumber(grade)
|
||||
userData.job.grade_name = gradeObject.name
|
||||
userData.job.grade_label = gradeObject.label
|
||||
userData.job.grade_salary = gradeObject.salary
|
||||
|
||||
userData.job.skin_male = {}
|
||||
userData.job.skin_female = {}
|
||||
|
||||
if gradeObject.skin_male then userData.job.skin_male = json.decode(gradeObject.skin_male) end
|
||||
if gradeObject.skin_female then userData.job.skin_female = json.decode(gradeObject.skin_female) end
|
||||
|
||||
-- Inventory
|
||||
if result.inventory and result.inventory ~= '' then
|
||||
local inventory = json.decode(result.inventory)
|
||||
|
||||
for name,count in pairs(inventory) do
|
||||
local item = ESX.Items[name]
|
||||
|
||||
if item then
|
||||
foundItems[name] = count
|
||||
else
|
||||
print(('[^3WARNING^7] Ignoring invalid item "%s" for "%s"'):format(name, identifier))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for name,item in pairs(ESX.Items) do
|
||||
local count = foundItems[name] or 0
|
||||
if count > 0 then userData.weight = userData.weight + (item.weight * count) end
|
||||
|
||||
table.insert(userData.inventory, {
|
||||
name = name,
|
||||
count = count,
|
||||
label = item.label,
|
||||
weight = item.weight,
|
||||
usable = Core.UsableItemsCallbacks[name] ~= nil,
|
||||
rare = item.rare,
|
||||
canRemove = item.canRemove
|
||||
})
|
||||
end
|
||||
|
||||
table.sort(userData.inventory, function(a, b)
|
||||
return a.label < b.label
|
||||
end)
|
||||
|
||||
-- Group
|
||||
if result.group then
|
||||
if result.group == "superadmin" then
|
||||
userData.group = "admin"
|
||||
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
|
||||
if not weapon.components then weapon.components = {} end
|
||||
if not weapon.tintIndex then weapon.tintIndex = 0 end
|
||||
|
||||
table.insert(userData.loadout, {
|
||||
name = name,
|
||||
ammo = weapon.ammo,
|
||||
label = label,
|
||||
components = weapon.components,
|
||||
tintIndex = weapon.tintIndex
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Position
|
||||
if result.position and result.position ~= '' then
|
||||
userData.coords = json.decode(result.position)
|
||||
else
|
||||
print('[^3WARNING^7] Column ^5"position"^0 in ^5"users"^0 table is missing required default value. Using backup coords, fix your database.')
|
||||
userData.coords = {x = -269.4, y = -955.3, z = 31.2, heading = 205.8}
|
||||
end
|
||||
|
||||
-- Skin
|
||||
if result.skin and result.skin ~= '' then
|
||||
userData.skin = json.decode(result.skin)
|
||||
else
|
||||
if userData.sex == 'f' then userData.skin = {sex=1} else userData.skin = {sex=0} end
|
||||
end
|
||||
|
||||
-- Identity
|
||||
if result.firstname and result.firstname ~= '' then
|
||||
userData.firstname = result.firstname
|
||||
userData.lastname = result.lastname
|
||||
userData.playerName = userData.firstname..' '..userData.lastname
|
||||
if result.dateofbirth then userData.dateofbirth = result.dateofbirth end
|
||||
if result.sex then userData.sex = result.sex end
|
||||
if result.height then userData.height = result.height end
|
||||
end
|
||||
|
||||
local xPlayer = CreateExtendedPlayer(playerId, identifier, userData.group, userData.accounts, userData.inventory, userData.weight, userData.job, userData.loadout, userData.playerName, userData.coords)
|
||||
ESX.Players[playerId] = xPlayer
|
||||
|
||||
if userData.firstname then
|
||||
xPlayer.set('firstName', userData.firstname)
|
||||
xPlayer.set('lastName', userData.lastname)
|
||||
if userData.dateofbirth then xPlayer.set('dateofbirth', userData.dateofbirth) end
|
||||
if userData.sex then xPlayer.set('sex', userData.sex) end
|
||||
if userData.height then xPlayer.set('height', userData.height) end
|
||||
end
|
||||
|
||||
TriggerEvent('esx:playerLoaded', playerId, xPlayer, isNew)
|
||||
|
||||
xPlayer.triggerEvent('esx:playerLoaded', {
|
||||
accounts = xPlayer.getAccounts(),
|
||||
coords = xPlayer.getCoords(),
|
||||
identifier = xPlayer.getIdentifier(),
|
||||
inventory = xPlayer.getInventory(),
|
||||
job = xPlayer.getJob(),
|
||||
loadout = xPlayer.getLoadout(),
|
||||
maxWeight = xPlayer.getMaxWeight(),
|
||||
money = xPlayer.getMoney(),
|
||||
dead = false
|
||||
}, isNew, userData.skin)
|
||||
|
||||
xPlayer.triggerEvent('esx:createMissingPickups', Core.Pickups)
|
||||
xPlayer.triggerEvent('esx:registerSuggestions', Core.RegisteredCommands)
|
||||
print(('[^2INFO^0] Player ^5"%s" ^0has connected to the server. ID: ^5%s^7'):format(xPlayer.getName(), playerId))
|
||||
end
|
||||
|
||||
AddEventHandler('chatMessage', function(playerId, author, message)
|
||||
@@ -319,7 +296,7 @@ AddEventHandler('playerDropped', function(reason)
|
||||
if xPlayer then
|
||||
TriggerEvent('esx:playerDropped', playerId, reason)
|
||||
|
||||
ESX.SavePlayer(xPlayer, function()
|
||||
Core.SavePlayer(xPlayer, function()
|
||||
ESX.Players[playerId] = nil
|
||||
end)
|
||||
end
|
||||
@@ -331,7 +308,7 @@ if Config.Multichar then
|
||||
if xPlayer then
|
||||
TriggerEvent('esx:playerDropped', playerId, reason)
|
||||
|
||||
ESX.SavePlayer(xPlayer, function()
|
||||
Core.SavePlayer(xPlayer, function()
|
||||
ESX.Players[playerId] = nil
|
||||
end)
|
||||
end
|
||||
@@ -512,7 +489,7 @@ end)
|
||||
|
||||
RegisterNetEvent('esx:onPickup')
|
||||
AddEventHandler('esx:onPickup', function(pickupId)
|
||||
local pickup, xPlayer, success = ESX.Pickups[pickupId], ESX.GetPlayerFromId(source)
|
||||
local pickup, xPlayer, success = Core.Pickups[pickupId], ESX.GetPlayerFromId(source)
|
||||
|
||||
if pickup then
|
||||
if pickup.type == 'item_standard' then
|
||||
@@ -540,7 +517,7 @@ AddEventHandler('esx:onPickup', function(pickupId)
|
||||
end
|
||||
|
||||
if success then
|
||||
ESX.Pickups[pickupId] = nil
|
||||
Core.Pickups[pickupId] = nil
|
||||
TriggerClientEvent('esx:removePickup', -1, pickupId)
|
||||
end
|
||||
end
|
||||
@@ -592,7 +569,7 @@ AddEventHandler('txAdmin:events:scheduledRestart', function(eventData)
|
||||
if eventData.secondsRemaining == 60 then
|
||||
Citizen.CreateThread(function()
|
||||
Citizen.Wait(50000)
|
||||
ESX.SavePlayers()
|
||||
Core.SavePlayers()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -9,17 +9,16 @@ if Config.UseDeferrals then
|
||||
Citizen.Wait(100)
|
||||
|
||||
if identifier then
|
||||
MySQL.Async.fetchAll('SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
if result[1].firstname then
|
||||
MySQL.single('SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?', {identifier},
|
||||
function(result)
|
||||
if result then
|
||||
if result.firstname then
|
||||
playerIdentity[identifier] = {
|
||||
firstName = result[1].firstname,
|
||||
lastName = result[1].lastname,
|
||||
dateOfBirth = result[1].dateofbirth,
|
||||
sex = result[1].sex,
|
||||
height = result[1].height
|
||||
firstName = result.firstname,
|
||||
lastName = result.lastname,
|
||||
dateOfBirth = result.dateofbirth,
|
||||
sex = result.sex,
|
||||
height = result.height
|
||||
}
|
||||
|
||||
deferrals.done()
|
||||
@@ -107,17 +106,16 @@ elseif not Config.UseDeferrals then
|
||||
Citizen.Wait(40)
|
||||
|
||||
if identifier then
|
||||
MySQL.Async.fetchAll('SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
if result[1].firstname then
|
||||
MySQL.single('SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?', {identifier},
|
||||
function(result)
|
||||
if result then
|
||||
if result.firstname then
|
||||
playerIdentity[identifier] = {
|
||||
firstName = result[1].firstname,
|
||||
lastName = result[1].lastname,
|
||||
dateOfBirth = result[1].dateofbirth,
|
||||
sex = result[1].sex,
|
||||
height = result[1].height
|
||||
firstName = result.firstname,
|
||||
lastName = result.lastname,
|
||||
dateOfBirth = result.dateofbirth,
|
||||
sex = result.sex,
|
||||
height = result.height
|
||||
}
|
||||
|
||||
alreadyRegistered[identifier] = true
|
||||
@@ -225,17 +223,16 @@ elseif not Config.UseDeferrals then
|
||||
end)
|
||||
|
||||
function checkIdentity(xPlayer)
|
||||
MySQL.Async.fetchAll('SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
if result[1].firstname then
|
||||
MySQL.single('SELECT firstname, lastname, dateofbirth, sex, height FROM users WHERE identifier = ?', {identifier},
|
||||
function(result)
|
||||
if result then
|
||||
if result.firstname then
|
||||
playerIdentity[xPlayer.identifier] = {
|
||||
firstName = result[1].firstname,
|
||||
lastName = result[1].lastname,
|
||||
dateOfBirth = result[1].dateofbirth,
|
||||
sex = result[1].sex,
|
||||
height = result[1].height
|
||||
firstName = result.firstname,
|
||||
lastName = result.lastname,
|
||||
dateOfBirth = result.dateofbirth,
|
||||
sex = result.sex,
|
||||
height = result.height
|
||||
}
|
||||
|
||||
alreadyRegistered[xPlayer.identifier] = true
|
||||
@@ -284,7 +281,7 @@ if Config.EnableCommands then
|
||||
ESX.RegisterCommand('chardel', 'user', function(xPlayer, args, showError)
|
||||
if xPlayer and xPlayer.getName() then
|
||||
if Config.UseDeferrals then
|
||||
xPlayer.kick(_('deleted_identity'))
|
||||
xPlayer.kick(_U('deleted_identity'))
|
||||
Citizen.Wait(1500)
|
||||
deleteIdentity(xPlayer)
|
||||
xPlayer.showNotification(_U('deleted_character'))
|
||||
@@ -367,61 +364,16 @@ function deleteIdentity(xPlayer)
|
||||
end
|
||||
|
||||
function saveIdentityToDatabase(identifier, identity)
|
||||
MySQL.Sync.execute('UPDATE users SET firstname = @firstname, lastname = @lastname, dateofbirth = @dateofbirth, sex = @sex, height = @height WHERE identifier = @identifier', {
|
||||
['@identifier'] = identifier,
|
||||
['@firstname'] = identity.firstName,
|
||||
['@lastname'] = identity.lastName,
|
||||
['@dateofbirth'] = identity.dateOfBirth,
|
||||
['@sex'] = identity.sex,
|
||||
['@height'] = identity.height
|
||||
})
|
||||
MySQL.update.await('UPDATE users SET firstname = ?, lastname = ?, dateofbirth = ?, sex = ?, height = ? WHERE identifier = ?', {identity.firstName, identity.lastName, identity.dateOfBirth, identity.sex, identity.height. identifier})
|
||||
end
|
||||
|
||||
function deleteIdentityFromDatabase(xPlayer)
|
||||
MySQL.Sync.execute('UPDATE users SET firstname = @firstname, lastname = @lastname, dateofbirth = @dateofbirth, sex = @sex, height = @height , skin = @skin WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@firstname'] = NULL,
|
||||
['@lastname'] = NULL,
|
||||
['@dateofbirth'] = NULL,
|
||||
['@sex'] = NULL,
|
||||
['@height'] = NULL,
|
||||
['@skin'] = NULL
|
||||
})
|
||||
MySQL.query.await('UPDATE users SET firstname = ?, lastname = ?, dateofbirth = ?, sex = ?, height = ?, skin = ? WHERE identifier = ?', {nil, nil, nil, nil, nil, xPlayer.identifier})
|
||||
|
||||
if Config.FullCharDelete then
|
||||
MySQL.Sync.execute('UPDATE addon_account_data SET money = 0 WHERE account_name = @account_name AND owner = @owner', {
|
||||
['@account_name'] = 'bank_savings',
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
MySQL.update.await('UPDATE addon_account_data SET money = 0 WHERE account_name IN (?) AND owner = ?', {{'bank_savings', 'caution'}, xPlayer.identifier})
|
||||
|
||||
MySQL.Sync.execute('UPDATE addon_account_data SET money = 0 WHERE account_name = @account_name AND owner = @owner', {
|
||||
['@account_name'] = 'caution',
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
|
||||
MySQL.Sync.execute('UPDATE datastore_data SET data = @data WHERE name = @name AND owner = @owner', {
|
||||
['@data'] = '\'{}\'',
|
||||
['@name'] = 'user_ears',
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
|
||||
MySQL.Sync.execute('UPDATE datastore_data SET data = @data WHERE name = @name AND owner = @owner', {
|
||||
['@data'] = '\'{}\'',
|
||||
['@name'] = 'user_glasses',
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
|
||||
MySQL.Sync.execute('UPDATE datastore_data SET data = @data WHERE name = @name AND owner = @owner', {
|
||||
['@data'] = '\'{}\'',
|
||||
['@name'] = 'user_helmet',
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
|
||||
MySQL.Sync.execute('UPDATE datastore_data SET data = @data WHERE name = @name AND owner = @owner', {
|
||||
['@data'] = '\'{}\'',
|
||||
['@name'] = 'user_mask',
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
MySQL.prepare.await('UPDATE datastore_data SET data = ? WHERE name IN (?) AND owner = ?', {'\'{}\'', {'user_ears', 'user_glasses', 'user_helmet', 'user_mask'}, xPlayer.identifier})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,97 +1,95 @@
|
||||
Citizen.CreateThread(function()
|
||||
local ESX = exports['es_extended']:getSharedObject()
|
||||
local GUI, MenuType = {}, 'default'
|
||||
GUI.Time = 0
|
||||
local ESX = exports.es_extended:getSharedObject()
|
||||
local GUI, MenuType = {}, 'default'
|
||||
GUI.Time = 0
|
||||
|
||||
local openMenu = function(namespace, name, data)
|
||||
SendNUIMessage({
|
||||
action = 'openMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
local openMenu = function(namespace, name, data)
|
||||
SendNUIMessage({
|
||||
action = 'openMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
end
|
||||
|
||||
local closeMenu = function(namespace, name)
|
||||
SendNUIMessage({
|
||||
action = 'closeMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
end
|
||||
|
||||
ESX.UI.Menu.RegisterType(MenuType, openMenu, closeMenu)
|
||||
|
||||
RegisterNUICallback('menu_submit', function(data, cb)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
if menu.submit ~= nil then
|
||||
menu.submit(data, menu)
|
||||
end
|
||||
cb('OK')
|
||||
end)
|
||||
|
||||
RegisterNUICallback('menu_cancel', function(data, cb)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
if menu.cancel ~= nil then
|
||||
menu.cancel(data, menu)
|
||||
end
|
||||
cb('OK')
|
||||
end)
|
||||
|
||||
RegisterNUICallback('menu_change', function(data, cb)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
for i=1, #data.elements, 1 do
|
||||
menu.setElement(i, 'value', data.elements[i].value)
|
||||
|
||||
if data.elements[i].selected then
|
||||
menu.setElement(i, 'selected', true)
|
||||
else
|
||||
menu.setElement(i, 'selected', false)
|
||||
end
|
||||
end
|
||||
|
||||
if menu.change ~= nil then
|
||||
menu.change(data, menu)
|
||||
end
|
||||
cb('OK')
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(15)
|
||||
|
||||
if IsControlPressed(0, 18) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'ENTER'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 177) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'BACKSPACE'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 27) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 200 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'TOP'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 173) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 200 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'DOWN'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 174) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'LEFT'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 175) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'RIGHT'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
end
|
||||
|
||||
local closeMenu = function(namespace, name)
|
||||
SendNUIMessage({
|
||||
action = 'closeMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
end
|
||||
|
||||
ESX.UI.Menu.RegisterType(MenuType, openMenu, closeMenu)
|
||||
|
||||
RegisterNUICallback('menu_submit', function(data, cb)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
if menu.submit ~= nil then
|
||||
menu.submit(data, menu)
|
||||
end
|
||||
cb('OK')
|
||||
end)
|
||||
|
||||
RegisterNUICallback('menu_cancel', function(data, cb)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
if menu.cancel ~= nil then
|
||||
menu.cancel(data, menu)
|
||||
end
|
||||
cb('OK')
|
||||
end)
|
||||
|
||||
RegisterNUICallback('menu_change', function(data, cb)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
for i=1, #data.elements, 1 do
|
||||
menu.setElement(i, 'value', data.elements[i].value)
|
||||
|
||||
if data.elements[i].selected then
|
||||
menu.setElement(i, 'selected', true)
|
||||
else
|
||||
menu.setElement(i, 'selected', false)
|
||||
end
|
||||
end
|
||||
|
||||
if menu.change ~= nil then
|
||||
menu.change(data, menu)
|
||||
end
|
||||
cb('OK')
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(15)
|
||||
|
||||
if IsControlPressed(0, 18) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'ENTER'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 177) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'BACKSPACE'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 27) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 200 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'TOP'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 173) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 200 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'DOWN'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 174) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'LEFT'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
|
||||
if IsControlPressed(0, 175) and IsInputDisabled(0) and (GetGameTimer() - GUI.Time) > 150 then
|
||||
SendNUIMessage({action = 'controlPressed', control = 'RIGHT'})
|
||||
GUI.Time = GetGameTimer()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -1,105 +1,103 @@
|
||||
local ESX = exports.es_extended:getSharedObject()
|
||||
local Timeouts, OpenedMenus, MenuType = {}, {}, 'dialog'
|
||||
|
||||
local openMenu = function(namespace, name, data)
|
||||
for i=1, #Timeouts, 1 do
|
||||
ESX.ClearTimeout(Timeouts[i])
|
||||
end
|
||||
|
||||
OpenedMenus[namespace .. '_' .. name] = true
|
||||
|
||||
SendNUIMessage({
|
||||
action = 'openMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
|
||||
local timeoutId = ESX.SetTimeout(200, function()
|
||||
SetNuiFocus(true, true)
|
||||
end)
|
||||
|
||||
table.insert(Timeouts, timeoutId)
|
||||
end
|
||||
|
||||
local closeMenu = function(namespace, name)
|
||||
OpenedMenus[namespace .. '_' .. name] = nil
|
||||
|
||||
SendNUIMessage({
|
||||
action = 'closeMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
|
||||
if ESX.Table.SizeOf(OpenedMenus) == 0 then
|
||||
SetNuiFocus(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ESX.UI.Menu.RegisterType(MenuType, openMenu, closeMenu)
|
||||
|
||||
AddEventHandler('esx_menu_dialog:message:menu_submit', function(data)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
local cancel = false
|
||||
|
||||
if menu.submit then
|
||||
-- is the submitted data a number?
|
||||
if tonumber(data.value) then
|
||||
data.value = ESX.Math.Round(tonumber(data.value))
|
||||
|
||||
-- check for negative value
|
||||
if tonumber(data.value) <= 0 then
|
||||
cancel = true
|
||||
end
|
||||
end
|
||||
|
||||
data.value = ESX.Math.Trim(data.value)
|
||||
|
||||
-- don't submit if the value is negative or if it's 0
|
||||
if cancel then
|
||||
ESX.ShowNotification('That input is not allowed!')
|
||||
else
|
||||
menu.submit(data, menu)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_menu_dialog:message:menu_cancel', function(data)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
if menu.cancel ~= nil then
|
||||
menu.cancel(data, menu)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_menu_dialog:message:menu_change', function(data)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
if menu.change ~= nil then
|
||||
menu.change(data, menu)
|
||||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
local ESX = exports['es_extended']:getSharedObject()
|
||||
local Timeouts, OpenedMenus, MenuType = {}, {}, 'dialog'
|
||||
while true do
|
||||
Citizen.Wait(10)
|
||||
|
||||
local openMenu = function(namespace, name, data)
|
||||
for i=1, #Timeouts, 1 do
|
||||
ESX.ClearTimeout(Timeouts[i])
|
||||
if ESX.Table.SizeOf(OpenedMenus) > 0 then
|
||||
DisableControlAction(0, 1, true) -- LookLeftRight
|
||||
DisableControlAction(0, 2, true) -- LookUpDown
|
||||
DisableControlAction(0, 142, true) -- MeleeAttackAlternate
|
||||
DisableControlAction(0, 106, true) -- VehicleMouseControlOverride
|
||||
DisableControlAction(0, 12, true) -- WeaponWheelUpDown
|
||||
DisableControlAction(0, 14, true) -- WeaponWheelNext
|
||||
DisableControlAction(0, 15, true) -- WeaponWheelPrev
|
||||
DisableControlAction(0, 16, true) -- SelectNextWeapon
|
||||
DisableControlAction(0, 17, true) -- SelectPrevWeapon
|
||||
else
|
||||
Citizen.Wait(500)
|
||||
end
|
||||
|
||||
OpenedMenus[namespace .. '_' .. name] = true
|
||||
|
||||
SendNUIMessage({
|
||||
action = 'openMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
|
||||
local timeoutId = ESX.SetTimeout(200, function()
|
||||
SetNuiFocus(true, true)
|
||||
end)
|
||||
|
||||
table.insert(Timeouts, timeoutId)
|
||||
end
|
||||
|
||||
local closeMenu = function(namespace, name)
|
||||
OpenedMenus[namespace .. '_' .. name] = nil
|
||||
|
||||
SendNUIMessage({
|
||||
action = 'closeMenu',
|
||||
namespace = namespace,
|
||||
name = name,
|
||||
data = data
|
||||
})
|
||||
|
||||
if ESX.Table.SizeOf(OpenedMenus) == 0 then
|
||||
SetNuiFocus(false)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ESX.UI.Menu.RegisterType(MenuType, openMenu, closeMenu)
|
||||
|
||||
AddEventHandler('esx_menu_dialog:message:menu_submit', function(data)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
local cancel = false
|
||||
|
||||
if menu.submit then
|
||||
-- is the submitted data a number?
|
||||
if tonumber(data.value) then
|
||||
data.value = ESX.Math.Round(tonumber(data.value))
|
||||
|
||||
-- check for negative value
|
||||
if tonumber(data.value) <= 0 then
|
||||
cancel = true
|
||||
end
|
||||
end
|
||||
|
||||
data.value = ESX.Math.Trim(data.value)
|
||||
|
||||
-- don't submit if the value is negative or if it's 0
|
||||
if cancel then
|
||||
ESX.ShowNotification('That input is not allowed!')
|
||||
else
|
||||
menu.submit(data, menu)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_menu_dialog:message:menu_cancel', function(data)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
if menu.cancel ~= nil then
|
||||
menu.cancel(data, menu)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_menu_dialog:message:menu_change', function(data)
|
||||
local menu = ESX.UI.Menu.GetOpened(MenuType, data._namespace, data._name)
|
||||
|
||||
if menu.change ~= nil then
|
||||
menu.change(data, menu)
|
||||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(10)
|
||||
|
||||
if ESX.Table.SizeOf(OpenedMenus) > 0 then
|
||||
DisableControlAction(0, 1, true) -- LookLeftRight
|
||||
DisableControlAction(0, 2, true) -- LookUpDown
|
||||
DisableControlAction(0, 142, true) -- MeleeAttackAlternate
|
||||
DisableControlAction(0, 106, true) -- VehicleMouseControlOverride
|
||||
DisableControlAction(0, 12, true) -- WeaponWheelUpDown
|
||||
DisableControlAction(0, 14, true) -- WeaponWheelNext
|
||||
DisableControlAction(0, 15, true) -- WeaponWheelPrev
|
||||
DisableControlAction(0, 16, true) -- SelectNextWeapon
|
||||
DisableControlAction(0, 17, true) -- SelectPrevWeapon
|
||||
else
|
||||
Citizen.Wait(500)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
@@ -10,7 +10,7 @@ AddEventHandler('esx_skin:save', function(skin)
|
||||
xPlayer.setMaxWeight(defaultMaxWeight)
|
||||
end
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET skin = @skin WHERE identifier = @identifier', {
|
||||
MySQL.update('UPDATE users SET skin = @skin WHERE identifier = @identifier', {
|
||||
['@skin'] = json.encode(skin),
|
||||
['@identifier'] = xPlayer.identifier
|
||||
})
|
||||
@@ -34,7 +34,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_skin:getPlayerSkin', function(source, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT skin FROM users WHERE identifier = @identifier', {
|
||||
MySQL.query('SELECT skin FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(users)
|
||||
local user, skin = users[1]
|
||||
|
||||
@@ -8,37 +8,25 @@ function CreateAddonAccount(name, owner, money)
|
||||
self.addMoney = function(m)
|
||||
self.money = self.money + m
|
||||
self.save()
|
||||
|
||||
TriggerClientEvent('esx_addonaccount:setMoney', -1, self.name, self.money)
|
||||
end
|
||||
|
||||
self.removeMoney = function(m)
|
||||
self.money = self.money - m
|
||||
self.save()
|
||||
|
||||
TriggerClientEvent('esx_addonaccount:setMoney', -1, self.name, self.money)
|
||||
end
|
||||
|
||||
self.setMoney = function(m)
|
||||
self.money = m
|
||||
self.save()
|
||||
|
||||
TriggerClientEvent('esx_addonaccount:setMoney', -1, self.name, self.money)
|
||||
end
|
||||
|
||||
self.save = function()
|
||||
if self.owner == nil then
|
||||
MySQL.Async.execute('UPDATE addon_account_data SET money = @money WHERE account_name = @account_name', {
|
||||
['@account_name'] = self.name,
|
||||
['@money'] = self.money
|
||||
})
|
||||
MySQL.update('UPDATE addon_account_data SET money = ? WHERE account_name = ?', {self.money, self.name})
|
||||
else
|
||||
MySQL.Async.execute('UPDATE addon_account_data SET money = @money WHERE account_name = @account_name AND owner = @owner', {
|
||||
['@account_name'] = self.name,
|
||||
['@money'] = self.money,
|
||||
['@owner'] = self.owner
|
||||
})
|
||||
MySQL.update('UPDATE addon_account_data SET money = ? WHERE account_name = ? AND owner = ?', {self.money, self.name, self.owner})
|
||||
end
|
||||
TriggerClientEvent('esx_addonaccount:setMoney', -1, self.name, self.money)
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@@ -1,41 +1,33 @@
|
||||
local AccountsIndex, Accounts, SharedAccounts = {}, {}, {}
|
||||
|
||||
MySQL.ready(function()
|
||||
local result = MySQL.Sync.fetchAll('SELECT * FROM addon_account')
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
local accounts = MySQL.query.await('SELECT * FROM addon_account LEFT JOIN addon_account_data ON addon_account.name = addon_account_data.account_name UNION SELECT * FROM addon_account RIGHT JOIN addon_account_data ON addon_account.name = addon_account_data.account_name')
|
||||
|
||||
for i=1, #result, 1 do
|
||||
local name = result[i].name
|
||||
local label = result[i].label
|
||||
local shared = result[i].shared
|
||||
|
||||
local result2 = MySQL.Sync.fetchAll('SELECT * FROM addon_account_data WHERE account_name = @account_name', {
|
||||
['@account_name'] = name
|
||||
})
|
||||
|
||||
if shared == 0 then
|
||||
table.insert(AccountsIndex, name)
|
||||
Accounts[name] = {}
|
||||
|
||||
for j=1, #result2, 1 do
|
||||
local addonAccount = CreateAddonAccount(name, result2[j].owner, result2[j].money)
|
||||
table.insert(Accounts[name], addonAccount)
|
||||
end
|
||||
else
|
||||
local money = nil
|
||||
|
||||
if #result2 == 0 then
|
||||
MySQL.Sync.execute('INSERT INTO addon_account_data (account_name, money, owner) VALUES (@account_name, @money, NULL)', {
|
||||
['@account_name'] = name,
|
||||
['@money'] = 0
|
||||
})
|
||||
|
||||
money = 0
|
||||
local newAccounts = {}
|
||||
for i = 1, #accounts do
|
||||
local account = accounts[i]
|
||||
if account.shared == 0 then
|
||||
if not Accounts[account.name] then
|
||||
AccountsIndex[#AccountsIndex + 1] = account.name
|
||||
Accounts[account.name] = {}
|
||||
end
|
||||
Accounts[account.name][#Accounts[account.name] + 1] = CreateAddonAccount(account.name, account.owner, account.money)
|
||||
else
|
||||
money = result2[1].money
|
||||
if account.money then
|
||||
SharedAccounts[account.name] = CreateAddonAccount(account.name, nil, account.money)
|
||||
else
|
||||
newAccounts[#newAccounts + 1] = {account.name, 0}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local addonAccount = CreateAddonAccount(name, nil, money)
|
||||
SharedAccounts[name] = addonAccount
|
||||
if next(newAccounts) then
|
||||
MySQL.prepare('INSERT INTO addon_account_data (account_name, money) VALUES (?, ?)', newAccounts)
|
||||
for i = 1, #newAccounts do
|
||||
local newAccount = newAccounts[i]
|
||||
SharedAccounts[newAccount[1]] = CreateAddonAccount(newAccount[1], nil, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -68,17 +60,13 @@ AddEventHandler('esx:playerLoaded', function(playerId, xPlayer)
|
||||
local account = GetAccount(name, xPlayer.identifier)
|
||||
|
||||
if account == nil then
|
||||
MySQL.Async.execute('INSERT INTO addon_account_data (account_name, money, owner) VALUES (@account_name, @money, @owner)', {
|
||||
['@account_name'] = name,
|
||||
['@money'] = 0,
|
||||
['@owner'] = xPlayer.identifier
|
||||
})
|
||||
MySQL.insert('INSERT INTO addon_account_data (account_name, money, owner) VALUES (?, ?, ?)', {name, 0, xPlayer.identifier})
|
||||
|
||||
account = CreateAddonAccount(name, xPlayer.identifier, 0)
|
||||
table.insert(Accounts[name], account)
|
||||
Accounts[name][#Accounts[name] + 1] = account
|
||||
end
|
||||
|
||||
table.insert(addonAccounts, account)
|
||||
addonAccounts[#addonAccounts + 1] = account
|
||||
end
|
||||
|
||||
xPlayer.set('addonAccounts', addonAccounts)
|
||||
|
||||
@@ -42,14 +42,14 @@ function CreateAddonInventory(name, owner, items)
|
||||
table.insert(self.items, item)
|
||||
|
||||
if self.owner == nil then
|
||||
MySQL.Async.execute('INSERT INTO addon_inventory_items (inventory_name, name, count) VALUES (@inventory_name, @item_name, @count)',
|
||||
MySQL.update('INSERT INTO addon_inventory_items (inventory_name, name, count) VALUES (@inventory_name, @item_name, @count)',
|
||||
{
|
||||
['@inventory_name'] = self.name,
|
||||
['@item_name'] = name,
|
||||
['@count'] = 0
|
||||
})
|
||||
else
|
||||
MySQL.Async.execute('INSERT INTO addon_inventory_items (inventory_name, name, count, owner) VALUES (@inventory_name, @item_name, @count, @owner)',
|
||||
MySQL.update('INSERT INTO addon_inventory_items (inventory_name, name, count, owner) VALUES (@inventory_name, @item_name, @count, @owner)',
|
||||
{
|
||||
['@inventory_name'] = self.name,
|
||||
['@item_name'] = name,
|
||||
@@ -63,13 +63,13 @@ function CreateAddonInventory(name, owner, items)
|
||||
|
||||
self.saveItem = function(name, count)
|
||||
if self.owner == nil then
|
||||
MySQL.Async.execute('UPDATE addon_inventory_items SET count = @count WHERE inventory_name = @inventory_name AND name = @item_name', {
|
||||
MySQL.update('UPDATE addon_inventory_items SET count = @count WHERE inventory_name = @inventory_name AND name = @item_name', {
|
||||
['@inventory_name'] = self.name,
|
||||
['@item_name'] = name,
|
||||
['@count'] = count
|
||||
})
|
||||
else
|
||||
MySQL.Async.execute('UPDATE addon_inventory_items SET count = @count WHERE inventory_name = @inventory_name AND name = @item_name AND owner = @owner', {
|
||||
MySQL.update('UPDATE addon_inventory_items SET count = @count WHERE inventory_name = @inventory_name AND name = @item_name AND owner = @owner', {
|
||||
['@inventory_name'] = self.name,
|
||||
['@item_name'] = name,
|
||||
['@count'] = count,
|
||||
|
||||
@@ -2,20 +2,20 @@ Items = {}
|
||||
local InventoriesIndex, Inventories, SharedInventories = {}, {}, {}
|
||||
|
||||
MySQL.ready(function()
|
||||
local items = MySQL.Sync.fetchAll('SELECT * FROM items')
|
||||
local items = MySQL.query.await('SELECT * FROM items')
|
||||
|
||||
for i=1, #items, 1 do
|
||||
Items[items[i].name] = items[i].label
|
||||
end
|
||||
|
||||
local result = MySQL.Sync.fetchAll('SELECT * FROM addon_inventory')
|
||||
local result = MySQL.query.await('SELECT * FROM addon_inventory')
|
||||
|
||||
for i=1, #result, 1 do
|
||||
local name = result[i].name
|
||||
local label = result[i].label
|
||||
local shared = result[i].shared
|
||||
|
||||
local result2 = MySQL.Sync.fetchAll('SELECT * FROM addon_inventory_items WHERE inventory_name = @inventory_name', {
|
||||
local result2 = MySQL.query.await('SELECT * FROM addon_inventory_items WHERE inventory_name = @inventory_name', {
|
||||
['@inventory_name'] = name
|
||||
})
|
||||
|
||||
|
||||
@@ -109,17 +109,17 @@ function OpenVehicleSpawnerMenu(type, hospital, part, partNum)
|
||||
end
|
||||
|
||||
function StoreNearbyVehicle(playerCoords)
|
||||
local vehicles, vehiclePlates = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}
|
||||
|
||||
if #vehicles > 0 then
|
||||
for k,v in ipairs(vehicles) do
|
||||
local vehicles, plates, index = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}, {}
|
||||
|
||||
if next(vehicles) then
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
|
||||
-- Make sure the vehicle we're saving is empty, or else it wont be deleted
|
||||
if GetVehicleNumberOfPassengers(v) == 0 and IsVehicleSeatFree(v, -1) then
|
||||
table.insert(vehiclePlates, {
|
||||
vehicle = v,
|
||||
plate = ESX.Math.Trim(GetVehicleNumberPlateText(v))
|
||||
})
|
||||
if GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1) then
|
||||
local plate = ESX.Math.Trim(GetVehicleNumberPlateText(vehicle))
|
||||
plates[#plates + 1] = plate
|
||||
index[plate] = vehicle
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -127,11 +127,11 @@ function StoreNearbyVehicle(playerCoords)
|
||||
return
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback('esx_ambulancejob:storeNearbyVehicle', function(storeSuccess, foundNum)
|
||||
if storeSuccess then
|
||||
local vehicleId = vehiclePlates[foundNum]
|
||||
ESX.TriggerServerCallback('esx_ambulancejob:storeNearbyVehicle', function(plate)
|
||||
if plate then
|
||||
local vehicleId = index[plate]
|
||||
local attempts = 0
|
||||
ESX.Game.DeleteVehicle(vehicleId.vehicle)
|
||||
ESX.Game.DeleteVehicle(vehicleId)
|
||||
isBusy = true
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
@@ -142,7 +142,7 @@ function StoreNearbyVehicle(playerCoords)
|
||||
end)
|
||||
|
||||
-- Workaround for vehicle not deleting when other players are near it.
|
||||
while DoesEntityExist(vehicleId.vehicle) do
|
||||
while DoesEntityExist(vehicleId) do
|
||||
Citizen.Wait(500)
|
||||
attempts = attempts + 1
|
||||
|
||||
@@ -153,9 +153,10 @@ function StoreNearbyVehicle(playerCoords)
|
||||
|
||||
vehicles = ESX.Game.GetVehiclesInArea(playerCoords, 30.0)
|
||||
if #vehicles > 0 then
|
||||
for k,v in ipairs(vehicles) do
|
||||
if ESX.Math.Trim(GetVehicleNumberPlateText(v)) == vehicleId.plate then
|
||||
ESX.Game.DeleteVehicle(v)
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
if ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)) == plate then
|
||||
ESX.Game.DeleteVehicle(vehicle)
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -167,7 +168,7 @@ function StoreNearbyVehicle(playerCoords)
|
||||
else
|
||||
ESX.ShowNotification(_U('garage_has_notstored'))
|
||||
end
|
||||
end, vehiclePlates)
|
||||
end, plates)
|
||||
end
|
||||
|
||||
function GetAvailableVehicleSpawnPoint(hospital, part, partNum)
|
||||
|
||||
@@ -178,14 +178,8 @@ ESX.RegisterServerCallback('esx_ambulancejob:buyJobVehicle', function(source, cb
|
||||
if xPlayer.getMoney() >= price then
|
||||
xPlayer.removeMoney(price)
|
||||
|
||||
MySQL.Async.execute('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (@owner, @vehicle, @plate, @type, @job, @stored)', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@vehicle'] = json.encode(vehicleProps),
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@type'] = type,
|
||||
['@job'] = xPlayer.job.name,
|
||||
['@stored'] = true
|
||||
}, function (rowsChanged)
|
||||
MySQL.insert('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (?, ?, ?, ?, ?, ?)', {xPlayer.identifier, json.encode(vehicleProps), vehicleProps.plate, type, xPlayer.job.name, true},
|
||||
function (rowsChanged)
|
||||
cb(true)
|
||||
end)
|
||||
else
|
||||
@@ -194,46 +188,32 @@ ESX.RegisterServerCallback('esx_ambulancejob:buyJobVehicle', function(source, cb
|
||||
end
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_ambulancejob:storeNearbyVehicle', function(source, cb, nearbyVehicles)
|
||||
ESX.RegisterServerCallback('esx_ambulancejob:storeNearbyVehicle', function(source, cb, plates)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
local foundPlate, foundNum
|
||||
|
||||
for k,v in ipairs(nearbyVehicles) do
|
||||
local result = MySQL.Sync.fetchAll('SELECT plate FROM owned_vehicles WHERE owner = @owner AND plate = @plate AND job = @job', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = v.plate,
|
||||
['@job'] = xPlayer.job.name
|
||||
})
|
||||
local plate = MySQL.scalar.await('SELECT plate FROM owned_vehicles WHERE owner = ? AND plate IN (?) AND job = ?', {xPlayer.identifier, plates, xPlayer.job.name})
|
||||
|
||||
if result[1] then
|
||||
foundPlate, foundNum = result[1].plate, k
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not foundPlate then
|
||||
cb(false)
|
||||
else
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = true WHERE owner = @owner AND plate = @plate AND job = @job', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = foundPlate,
|
||||
['@job'] = xPlayer.job.name
|
||||
}, function (rowsChanged)
|
||||
if plate then
|
||||
MySQL.update('UPDATE owned_vehicles SET `stored` = true WHERE owner = ? AND plate = ? AND job = ?', {xPlayer.identifier, plate, xPlayer.job.name},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 0 then
|
||||
cb(false)
|
||||
else
|
||||
cb(true, foundNum)
|
||||
cb(plate)
|
||||
end
|
||||
end)
|
||||
else
|
||||
cb(false)
|
||||
end
|
||||
end)
|
||||
|
||||
function getPriceFromHash(vehicleHash, jobGrade, type)
|
||||
local vehicles = Config.AuthorizedVehicles[type][jobGrade]
|
||||
|
||||
for k,v in ipairs(vehicles) do
|
||||
if GetHashKey(v.model) == vehicleHash then
|
||||
return v.price
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
if GetHashKey(vehicle.model) == vehicleHash then
|
||||
return vehicle.price
|
||||
end
|
||||
end
|
||||
|
||||
@@ -306,10 +286,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_ambulancejob:getDeathStatus', function(source, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchScalar('SELECT is_dead FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(isDead)
|
||||
|
||||
MySQL.scalar('SELECT is_dead FROM users WHERE identifier = ?', {xPlayer.identifier}, function(isDead)
|
||||
if isDead then
|
||||
print(('[esx_ambulancejob] [^2INFO^7] "%s" attempted combat logging'):format(xPlayer.identifier))
|
||||
end
|
||||
@@ -323,9 +300,6 @@ AddEventHandler('esx_ambulancejob:setDeathStatus', function(isDead)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
if type(isDead) == 'boolean' then
|
||||
MySQL.Sync.execute('UPDATE users SET is_dead = @isDead WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@isDead'] = isDead
|
||||
})
|
||||
MySQL.update('UPDATE users SET is_dead = ? WHERE identifier = ?', {isDead, xPlayer.identifier})
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -56,7 +56,7 @@ end)
|
||||
function CalculateBankSavings(d, h, m)
|
||||
local asyncTasks = {}
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM addon_account_data WHERE account_name = @account_name', {
|
||||
MySQL.query('SELECT * FROM addon_account_data WHERE account_name = @account_name', {
|
||||
['@account_name'] = 'bank_savings'
|
||||
}, function(result)
|
||||
local bankInterests = 0
|
||||
@@ -80,7 +80,7 @@ function CalculateBankSavings(d, h, m)
|
||||
|
||||
local scope = function(newMoney, owner)
|
||||
table.insert(asyncTasks, function(cb)
|
||||
MySQL.Async.execute('UPDATE addon_account_data SET money = @money WHERE owner = @owner AND account_name = @account_name', {
|
||||
MySQL.update('UPDATE addon_account_data SET money = @money WHERE owner = @owner AND account_name = @account_name', {
|
||||
['@money'] = newMoney,
|
||||
['@owner'] = owner,
|
||||
['@account_name'] = 'bank_savings',
|
||||
|
||||
@@ -7,25 +7,13 @@ AddEventHandler('esx_billing:sendBill', function(playerId, sharedAccountName, la
|
||||
if amount > 0 and xTarget then
|
||||
TriggerEvent('esx_addonaccount:getSharedAccount', sharedAccountName, function(account)
|
||||
if account then
|
||||
MySQL.Async.execute('INSERT INTO billing (identifier, sender, target_type, target, label, amount) VALUES (@identifier, @sender, @target_type, @target, @label, @amount)', {
|
||||
['@identifier'] = xTarget.identifier,
|
||||
['@sender'] = xPlayer.identifier,
|
||||
['@target_type'] = 'society',
|
||||
['@target'] = sharedAccountName,
|
||||
['@label'] = label,
|
||||
['@amount'] = amount
|
||||
}, function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO billing (identifier, sender, target_type, target, label, amount) VALUES (?, ?, ?, ?, ?, ?)', {xTarget.identifier, xPlayer.identifier, 'society', sharedAccountName, label, amount},
|
||||
function(rowsChanged)
|
||||
xTarget.showNotification(_U('received_invoice'))
|
||||
end)
|
||||
else
|
||||
MySQL.Async.execute('INSERT INTO billing (identifier, sender, target_type, target, label, amount) VALUES (@identifier, @sender, @target_type, @target, @label, @amount)', {
|
||||
['@identifier'] = xTarget.identifier,
|
||||
['@sender'] = xPlayer.identifier,
|
||||
['@target_type'] = 'player',
|
||||
['@target'] = xPlayer.identifier,
|
||||
['@label'] = label,
|
||||
['@amount'] = amount
|
||||
}, function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO billing (identifier, sender, target_type, target, label, amount) VALUES (?, ?, ?, ?, ?, ?)', {xTarget.identifier, xPlayer.identifier, 'player', xPlayer.identifier, label, amount},
|
||||
function(rowsChanged)
|
||||
xTarget.showNotification(_U('received_invoice'))
|
||||
end)
|
||||
end
|
||||
@@ -36,9 +24,8 @@ end)
|
||||
ESX.RegisterServerCallback('esx_billing:getBills', function(source, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT amount, id, label FROM billing WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
MySQL.query('SELECT amount, id, label FROM billing WHERE identifier = ?', {xPlayer.identifier},
|
||||
function(result)
|
||||
cb(result)
|
||||
end)
|
||||
end)
|
||||
@@ -47,9 +34,8 @@ ESX.RegisterServerCallback('esx_billing:getTargetBills', function(source, cb, ta
|
||||
local xPlayer = ESX.GetPlayerFromId(target)
|
||||
|
||||
if xPlayer then
|
||||
MySQL.Async.fetchAll('SELECT amount, id, label FROM billing WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
MySQL.query('SELECT amount, id, label FROM billing WHERE identifier = ?', {xPlayer.identifier},
|
||||
function(result)
|
||||
cb(result)
|
||||
end)
|
||||
else
|
||||
@@ -60,19 +46,17 @@ end)
|
||||
ESX.RegisterServerCallback('esx_billing:payBill', function(source, cb, billId)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT sender, target_type, target, amount FROM billing WHERE id = @id', {
|
||||
['@id'] = billId
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
local amount = result[1].amount
|
||||
local xTarget = ESX.GetPlayerFromIdentifier(result[1].sender)
|
||||
MySQL.single('SELECT sender, target_type, target, amount FROM billing WHERE id = ?', {billId},
|
||||
function(result)
|
||||
if result then
|
||||
local amount = result.amount
|
||||
local xTarget = ESX.GetPlayerFromIdentifier(result.sender)
|
||||
|
||||
if result[1].target_type == 'player' then
|
||||
if result.target_type == 'player' then
|
||||
if xTarget then
|
||||
if xPlayer.getMoney() >= amount then
|
||||
MySQL.Async.execute('DELETE FROM billing WHERE id = @id', {
|
||||
['@id'] = billId
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('DELETE FROM billing WHERE id = ?', {billId},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
xPlayer.removeMoney(amount)
|
||||
xTarget.addMoney(amount)
|
||||
@@ -84,9 +68,8 @@ ESX.RegisterServerCallback('esx_billing:payBill', function(source, cb, billId)
|
||||
cb()
|
||||
end)
|
||||
elseif xPlayer.getAccount('bank').money >= amount then
|
||||
MySQL.Async.execute('DELETE FROM billing WHERE id = @id', {
|
||||
['@id'] = billId
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('DELETE FROM billing WHERE id = ?', {billId},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
xPlayer.removeAccountMoney('bank', amount)
|
||||
xTarget.addAccountMoney('bank', amount)
|
||||
@@ -109,9 +92,8 @@ ESX.RegisterServerCallback('esx_billing:payBill', function(source, cb, billId)
|
||||
else
|
||||
TriggerEvent('esx_addonaccount:getSharedAccount', result[1].target, function(account)
|
||||
if xPlayer.getMoney() >= amount then
|
||||
MySQL.Async.execute('DELETE FROM billing WHERE id = @id', {
|
||||
['@id'] = billId
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('DELETE FROM billing WHERE id = ?', {billId},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
xPlayer.removeMoney(amount)
|
||||
account.addMoney(amount)
|
||||
@@ -125,9 +107,8 @@ ESX.RegisterServerCallback('esx_billing:payBill', function(source, cb, billId)
|
||||
cb()
|
||||
end)
|
||||
elseif xPlayer.getAccount('bank').money >= amount then
|
||||
MySQL.Async.execute('DELETE FROM billing WHERE id = @id', {
|
||||
['@id'] = billId
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('DELETE FROM billing WHERE id = ?', {billId},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
xPlayer.removeAccountMoney('bank', amount)
|
||||
account.addMoney(amount)
|
||||
|
||||
@@ -3,7 +3,7 @@ MySQL.ready(function()
|
||||
end)
|
||||
|
||||
function ParkBoats()
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = true WHERE `stored` = false AND type = @type', {
|
||||
MySQL.update('UPDATE owned_vehicles SET `stored` = true WHERE `stored` = false AND type = @type', {
|
||||
['@type'] = 'boat'
|
||||
}, function (rowsChanged)
|
||||
if rowsChanged > 0 then
|
||||
@@ -24,7 +24,7 @@ ESX.RegisterServerCallback('esx_boat:buyBoat', function(source, cb, vehicleProps
|
||||
if xPlayer.getMoney() >= price then
|
||||
xPlayer.removeMoney(price)
|
||||
|
||||
MySQL.Async.execute('INSERT INTO owned_vehicles (owner, plate, vehicle, type, `stored`) VALUES (@owner, @plate, @vehicle, @type, @stored)', {
|
||||
MySQL.update('INSERT INTO owned_vehicles (owner, plate, vehicle, type, `stored`) VALUES (@owner, @plate, @vehicle, @type, @stored)', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@vehicle'] = json.encode(vehicleProps),
|
||||
@@ -43,7 +43,7 @@ RegisterServerEvent('esx_boat:takeOutVehicle')
|
||||
AddEventHandler('esx_boat:takeOutVehicle', function(plate)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = @stored WHERE owner = @owner AND plate = @plate', {
|
||||
MySQL.update('UPDATE owned_vehicles SET `stored` = @stored WHERE owner = @owner AND plate = @plate', {
|
||||
['@stored'] = false,
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = plate
|
||||
@@ -57,7 +57,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_boat:storeVehicle', function (source, cb, plate)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = @stored WHERE owner = @owner AND plate = @plate', {
|
||||
MySQL.update('UPDATE owned_vehicles SET `stored` = @stored WHERE owner = @owner AND plate = @plate', {
|
||||
['@stored'] = true,
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = plate
|
||||
@@ -73,7 +73,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_boat:getGarage', function(source, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT vehicle FROM owned_vehicles WHERE owner = @owner AND type = @type AND `stored` = @stored', {
|
||||
MySQL.query('SELECT vehicle FROM owned_vehicles WHERE owner = @owner AND type = @type AND `stored` = @stored', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@type'] = 'boat',
|
||||
['@stored'] = true
|
||||
|
||||
@@ -68,16 +68,9 @@ function CreateDataStore(name, owner, data)
|
||||
|
||||
local timeoutCallback = ESX.SetTimeout(10000, function()
|
||||
if self.owner == nil then
|
||||
MySQL.Async.execute('UPDATE datastore_data SET data = @data WHERE name = @name', {
|
||||
['@data'] = json.encode(self.data),
|
||||
['@name'] = self.name,
|
||||
})
|
||||
MySQL.update('UPDATE datastore_data SET data = ? WHERE name = ?', {json.encode(self.data), self.name})
|
||||
else
|
||||
MySQL.Async.execute('UPDATE datastore_data SET data = @data WHERE name = @name and owner = @owner', {
|
||||
['@data'] = json.encode(self.data),
|
||||
['@name'] = self.name,
|
||||
['@owner'] = self.owner,
|
||||
})
|
||||
MySQL.update('UPDATE datastore_data SET data = ? WHERE name = ? and owner = ?', {json.encode(self.data), self.name, self.owner})
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
@@ -1,41 +1,33 @@
|
||||
local DataStores, DataStoresIndex, SharedDataStores = {}, {}, {}
|
||||
|
||||
MySQL.ready(function()
|
||||
local result = MySQL.Sync.fetchAll('SELECT * FROM datastore')
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
local dataStore = MySQL.query.await('SELECT * FROM datastore_data LEFT JOIN datastore ON datastore_data.name = datastore.name UNION SELECT * FROM datastore_data RIGHT JOIN datastore ON datastore_data.name = datastore.name')
|
||||
|
||||
for i=1, #result, 1 do
|
||||
local name, label, shared = result[i].name, result[i].label, result[i].shared
|
||||
local result2 = MySQL.Sync.fetchAll('SELECT * FROM datastore_data WHERE name = @name', {
|
||||
['@name'] = name
|
||||
})
|
||||
|
||||
if shared == 0 then
|
||||
table.insert(DataStoresIndex, name)
|
||||
DataStores[name] = {}
|
||||
|
||||
for j=1, #result2, 1 do
|
||||
local storeName = result2[j].name
|
||||
local storeOwner = result2[j].owner
|
||||
local storeData = (result2[j].data == nil and {} or json.decode(result2[j].data))
|
||||
local dataStore = CreateDataStore(storeName, storeOwner, storeData)
|
||||
|
||||
table.insert(DataStores[name], dataStore)
|
||||
end
|
||||
else
|
||||
local data
|
||||
|
||||
if #result2 == 0 then
|
||||
MySQL.Sync.execute('INSERT INTO datastore_data (name, owner, data) VALUES (@name, NULL, \'{}\')', {
|
||||
['@name'] = name
|
||||
})
|
||||
|
||||
data = {}
|
||||
local newData = {}
|
||||
for i = 1, #dataStore do
|
||||
local data = dataStore[i]
|
||||
if data.shared == 0 then
|
||||
if not DataStores[data.name] then
|
||||
DataStoresIndex[#DataStoresIndex + 1] = data.name
|
||||
DataStores[data.name] = {}
|
||||
end
|
||||
DataStores[data.name][#DataStores[data.name] + 1] = CreateDataStore(data.name, data.owner, data.data)
|
||||
else
|
||||
data = json.decode(result2[1].data)
|
||||
if data.data then
|
||||
SharedDataStores[data.name] = CreateDataStore(data.name, nil, data.data)
|
||||
else
|
||||
newData[#newData + 1] = {data.name, '\'{}\''}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local dataStore = CreateDataStore(name, nil, data)
|
||||
SharedDataStores[name] = dataStore
|
||||
if next(newData) then
|
||||
MySQL.prepare('INSERT INTO datastore_data (name, data) VALUES (?, ?)', newData)
|
||||
for i = 1, #newData do
|
||||
local new = newData[i]
|
||||
SharedDataStores[new[1]] = CreateDataStore(new[1], nil, new[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -80,14 +72,9 @@ AddEventHandler('esx:playerLoaded', function(playerId, xPlayer)
|
||||
local dataStore = GetDataStore(name, xPlayer.identifier)
|
||||
|
||||
if not dataStore then
|
||||
MySQL.Async.execute('INSERT INTO datastore_data (name, owner, data) VALUES (@name, @owner, @data)', {
|
||||
['@name'] = name,
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@data'] = '{}'
|
||||
})
|
||||
MySQL.insert('INSERT INTO datastore_data (name, owner, data) VALUES (?, ?, ?)', {name, xPlayer.identifier, '{}'})
|
||||
|
||||
dataStore = CreateDataStore(name, xPlayer.identifier, {})
|
||||
table.insert(DataStores[name], dataStore)
|
||||
DataStores[name][#DataStores[name] + 1] = CreateDataStore(name, xPlayer.identifier, {})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -5,7 +5,7 @@ AddEventHandler('esx_garage:setParking', function(garage, zone, vehicleProps)
|
||||
|
||||
if vehicleProps == false then
|
||||
|
||||
MySQL.Async.execute('DELETE FROM `user_parkings` WHERE `identifier` = @identifier AND `garage` = @garage AND zone = @zone',
|
||||
MySQL.update('DELETE FROM `user_parkings` WHERE `identifier` = @identifier AND `garage` = @garage AND zone = @zone',
|
||||
{
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@garage'] = garage;
|
||||
@@ -14,7 +14,7 @@ AddEventHandler('esx_garage:setParking', function(garage, zone, vehicleProps)
|
||||
xPlayer.showNotification(_U('veh_released'))
|
||||
end)
|
||||
else
|
||||
MySQL.Async.execute('INSERT INTO `user_parkings` (`identifier`, `garage`, `zone`, `vehicle`) VALUES (@identifier, @garage, @zone, @vehicle)',
|
||||
MySQL.update('INSERT INTO `user_parkings` (`identifier`, `garage`, `zone`, `vehicle`) VALUES (@identifier, @garage, @zone, @vehicle)',
|
||||
{
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@garage'] = garage;
|
||||
@@ -28,7 +28,7 @@ end)
|
||||
|
||||
RegisterServerEvent('esx_garage:updateOwnedVehicle')
|
||||
AddEventHandler('esx_garage:updateOwnedVehicle', function(vehicleProps)
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET vehicle = @vehicle WHERE plate = @plate', {
|
||||
MySQL.update('UPDATE owned_vehicles SET vehicle = @vehicle WHERE plate = @plate', {
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@vehicle'] = json.encode(vehicleProps)
|
||||
})
|
||||
@@ -37,7 +37,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:getVehiclesInGarage', function(source, cb, garage)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM `user_parkings` WHERE `identifier` = @identifier AND garage = @garage',
|
||||
MySQL.query('SELECT * FROM `user_parkings` WHERE `identifier` = @identifier AND garage = @garage',
|
||||
{
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@garage'] = garage
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local availableJobs = {}
|
||||
|
||||
MySQL.ready(function()
|
||||
MySQL.Async.fetchAll('SELECT name, label FROM jobs WHERE whitelisted = @whitelisted', {
|
||||
MySQL.query('SELECT name, label FROM jobs WHERE whitelisted = @whitelisted', {
|
||||
['@whitelisted'] = false
|
||||
}, function(result)
|
||||
for i=1, #result, 1 do
|
||||
|
||||
@@ -149,7 +149,6 @@ Config.Jobs.fisherman = {
|
||||
Pos = {x = -1012.64, y = -1354.62, z = 5.54},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Size = {x = 5.0, y = 5.0, z = 3.0},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Marker= 1,
|
||||
Blip = true,
|
||||
Name = _U('delivery_point'),
|
||||
|
||||
@@ -196,7 +196,6 @@ Config.Jobs.miner = {
|
||||
Pos = {x = -148.78, y = -1040.38, z = 26.27},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Size = {x = 5.0, y = 5.0, z = 3.0},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Marker = 1,
|
||||
Blip = true,
|
||||
Name = _U('m_sell_iron'),
|
||||
@@ -222,7 +221,6 @@ Config.Jobs.miner = {
|
||||
Pos = {x = 261.48, y = 207.35, z = 109.28},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Size = {x = 5.0, y = 5.0, z = 3.0},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Marker = 1,
|
||||
Blip = true,
|
||||
Name = _U('m_sell_gold'),
|
||||
@@ -248,7 +246,6 @@ Config.Jobs.miner = {
|
||||
Pos = {x = -621.04, y = -228.53, z = 37.05},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Size = {x = 5.0, y = 5.0, z = 3.0},
|
||||
Color = {r = 204, g = 204, b = 0},
|
||||
Marker = 1,
|
||||
Blip = true,
|
||||
Name = _U('m_sell_diamond'),
|
||||
|
||||
@@ -2,10 +2,8 @@ function AddLicense(target, type, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(target)
|
||||
|
||||
if xPlayer then
|
||||
MySQL.Async.execute('INSERT INTO user_licenses (type, owner) VALUES (@type, @owner)', {
|
||||
['@type'] = type,
|
||||
['@owner'] = xPlayer.identifier
|
||||
}, function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO user_licenses (type, owner) VALUES (?, ?)', {type, xPlayer.identifier},
|
||||
function(rowsChanged)
|
||||
if cb then
|
||||
cb()
|
||||
end
|
||||
@@ -21,10 +19,8 @@ function RemoveLicense(target, type, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(target)
|
||||
|
||||
if xPlayer then
|
||||
MySQL.Async.execute('DELETE FROM user_licenses WHERE type = @type AND owner = @owner', {
|
||||
['@type'] = type,
|
||||
['@owner'] = xPlayer.identifier
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('DELETE FROM user_licenses WHERE type = ? AND owner = ?', {type, xPlayer.identifier},
|
||||
function(rowsChanged)
|
||||
if cb then
|
||||
cb()
|
||||
end
|
||||
@@ -37,48 +33,18 @@ function RemoveLicense(target, type, cb)
|
||||
end
|
||||
|
||||
function GetLicense(type, cb)
|
||||
MySQL.Async.fetchAll('SELECT label FROM licenses WHERE type = @type', {
|
||||
['@type'] = type
|
||||
}, function(result)
|
||||
local data = {
|
||||
type = type,
|
||||
label = result[1].label
|
||||
}
|
||||
|
||||
cb(data)
|
||||
MySQL.scalar('SELECT label FROM licenses WHERE type = ?', {type},
|
||||
function(result)
|
||||
cb({type = type, label = result})
|
||||
end)
|
||||
end
|
||||
|
||||
function GetLicenses(target, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(target)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT type FROM user_licenses WHERE owner = @owner', {
|
||||
['@owner'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
local licenses, asyncTasks = {}, {}
|
||||
|
||||
for i=1, #result, 1 do
|
||||
local scope = function(type)
|
||||
table.insert(asyncTasks, function(cb)
|
||||
MySQL.Async.fetchAll('SELECT label FROM licenses WHERE type = @type', {
|
||||
['@type'] = type
|
||||
}, function(result2)
|
||||
table.insert(licenses, {
|
||||
type = type,
|
||||
label = result2[1].label
|
||||
})
|
||||
|
||||
cb()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
scope(result[i].type)
|
||||
end
|
||||
|
||||
Async.parallel(asyncTasks, function(results)
|
||||
cb(licenses)
|
||||
end)
|
||||
MySQL.query('SELECT user_licenses.type, licenses.label FROM user_licenses LEFT JOIN licenses ON user_licenses.type = licenses.type WHERE owner = ?', {xPlayer.identifier},
|
||||
function(result)
|
||||
cb(result)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -86,11 +52,9 @@ function CheckLicense(target, type, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(target)
|
||||
|
||||
if xPlayer then
|
||||
MySQL.Async.fetchAll('SELECT COUNT(*) as count FROM user_licenses WHERE type = @type AND owner = @owner', {
|
||||
['@type'] = type,
|
||||
['@owner'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
if tonumber(result[1].count) > 0 then
|
||||
MySQL.scalar('SELECT type FROM user_licenses WHERE type = ? AND owner = ?', {type, xPlayer.identifier},
|
||||
function(result)
|
||||
if result then
|
||||
cb(true)
|
||||
else
|
||||
cb(false)
|
||||
@@ -102,19 +66,9 @@ function CheckLicense(target, type, cb)
|
||||
end
|
||||
|
||||
function GetLicensesList(cb)
|
||||
MySQL.Async.fetchAll('SELECT type, label FROM licenses', {
|
||||
['@type'] = type
|
||||
}, function(result)
|
||||
local licenses = {}
|
||||
|
||||
for i=1, #result, 1 do
|
||||
table.insert(licenses, {
|
||||
type = result[i].type,
|
||||
label = result[i].label
|
||||
})
|
||||
end
|
||||
|
||||
cb(licenses)
|
||||
MySQL.query('SELECT type, label FROM licenses',
|
||||
function(result)
|
||||
cb(result)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -37,17 +37,13 @@ RegisterServerEvent('esx_lscustom:refreshOwnedVehicle')
|
||||
AddEventHandler('esx_lscustom:refreshOwnedVehicle', function(vehicleProps)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT vehicle FROM owned_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = vehicleProps.plate
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
local vehicle = json.decode(result[1].vehicle)
|
||||
MySQL.single('SELECT vehicle FROM owned_vehicles WHERE plate = ?', {vehicleProps.plate},
|
||||
function(result)
|
||||
if result then
|
||||
local vehicle = json.decode(result.vehicle)
|
||||
|
||||
if vehicleProps.model == vehicle.model then
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET vehicle = @vehicle WHERE plate = @plate', {
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@vehicle'] = json.encode(vehicleProps)
|
||||
})
|
||||
MySQL.update('UPDATE owned_vehicles SET vehicle = ? WHERE plate = ?', {json.encode(vehicleProps), vehicleProps.plate})
|
||||
else
|
||||
print(('esx_lscustom: %s attempted to upgrade vehicle with mismatching vehicle model!'):format(xPlayer.identifier))
|
||||
end
|
||||
@@ -57,20 +53,7 @@ end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_lscustom:getVehiclesPrices', function(source, cb)
|
||||
if not Vehicles then
|
||||
MySQL.Async.fetchAll('SELECT * FROM vehicles', {}, function(result)
|
||||
local vehicles = {}
|
||||
|
||||
for i=1, #result, 1 do
|
||||
table.insert(vehicles, {
|
||||
model = result[i].model,
|
||||
price = result[i].price
|
||||
})
|
||||
end
|
||||
|
||||
Vehicles = vehicles
|
||||
cb(Vehicles)
|
||||
end)
|
||||
else
|
||||
cb(Vehicles)
|
||||
Vehicles = MySQL.query.await('SELECT model, price FROM vehicles')
|
||||
end
|
||||
cb(Vehicles)
|
||||
end)
|
||||
@@ -73,7 +73,7 @@ Locales['fr'] = {
|
||||
['not_enough_gas_can'] = 'Vous n\'avez ~r~pas assez~s~ de bouteille de gaz',
|
||||
['assembling_blowtorch'] = 'Assemblage de ~b~chalumeaux~s~...',
|
||||
['not_enough_repair_tools'] = 'Vous n\'avez ~r~pas assez~s~ d\'outils réparation',
|
||||
['assembling_blowtorch'] = 'Assemblage de ~b~kit réparation~s~...',
|
||||
['assembling_repair_kit'] = 'Assemblage de ~b~kit réparation~s~...',
|
||||
['not_enough_body_tools'] = 'Vous n\'avez ~r~pas assez~s~ d\'outils carosserie',
|
||||
['assembling_body_kit'] = 'Assemblage de ~b~kit carosserie~s~...',
|
||||
['your_comp_earned'] = 'Votre société a ~g~gagné~s~ ~g~$',
|
||||
|
||||
Submodule
+1
Submodule [esx_addons]/esx_multicharacter added at 02352a8e18
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -1,326 +0,0 @@
|
||||
ESX = exports['es_extended']:getSharedObject()
|
||||
if ESX.GetConfig().Multichar then
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while not ESX.PlayerLoaded do
|
||||
Citizen.Wait(0)
|
||||
if NetworkIsPlayerActive(PlayerId()) then
|
||||
exports.spawnmanager:setAutoSpawn(false)
|
||||
DoScreenFadeOut(0)
|
||||
while not GetResourceState('esx_menu_default') == 'started' do
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
TriggerEvent("esx_multicharacter:SetupCharacters")
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local canRelog, cam, Spawned = true
|
||||
local Characters = {}
|
||||
|
||||
RegisterNetEvent('esx_multicharacter:SetupCharacters')
|
||||
AddEventHandler('esx_multicharacter:SetupCharacters', function()
|
||||
ESX.PlayerLoaded = false
|
||||
ESX.PlayerData = {}
|
||||
Spawned = false
|
||||
cam = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
|
||||
local playerPed = PlayerPedId()
|
||||
SetEntityCoords(playerPed, Config.Spawn.x, Config.Spawn.y, Config.Spawn.z, true, false, false, false)
|
||||
SetEntityHeading(playerPed, Config.Spawn.w)
|
||||
local offset = GetOffsetFromEntityInWorldCoords(playerPed, 0, 1.7, 0.4)
|
||||
DoScreenFadeOut(0)
|
||||
SetCamActive(cam, true)
|
||||
RenderScriptCams(true, false, 1, true, true)
|
||||
SetCamCoord(cam, offset.x, offset.y, offset.z)
|
||||
PointCamAtCoord(cam, Config.Spawn.x, Config.Spawn.y, Config.Spawn.z + 1.3)
|
||||
|
||||
ESX.UI.HUD.SetDisplay(0.0)
|
||||
StartLoop()
|
||||
ShutdownLoadingScreen()
|
||||
ShutdownLoadingScreenNui()
|
||||
TriggerEvent('esx:loadingScreenOff')
|
||||
Citizen.Wait(200)
|
||||
TriggerServerEvent("esx_multicharacter:SetupCharacters")
|
||||
end)
|
||||
|
||||
StartLoop = function()
|
||||
hidePlayers = true
|
||||
MumbleSetVolumeOverride(PlayerId(), 0.0)
|
||||
Citizen.CreateThread(function()
|
||||
local keys = {18, 27, 172, 173, 174, 175, 176, 177, 187, 188, 191, 201, 108, 109}
|
||||
while hidePlayers do
|
||||
DisableAllControlActions(0)
|
||||
for i=1, #keys do
|
||||
EnableControlAction(0, keys[i], true)
|
||||
end
|
||||
SetEntityVisible(PlayerPedId(), 0, 0)
|
||||
SetLocalPlayerVisibleLocally(1)
|
||||
SetPlayerInvincible(PlayerId(), 1)
|
||||
ThefeedHideThisFrame()
|
||||
HideHudComponentThisFrame(11)
|
||||
HideHudComponentThisFrame(12)
|
||||
HideHudComponentThisFrame(21)
|
||||
HideHudAndRadarThisFrame()
|
||||
Citizen.Wait(3)
|
||||
local vehicles = GetGamePool('CVehicle')
|
||||
for i=1, #vehicles do
|
||||
SetEntityLocallyInvisible(vehicles[i])
|
||||
end
|
||||
end
|
||||
local playerId, playerPed = PlayerId(), PlayerPedId()
|
||||
MumbleSetVolumeOverride(playerId, -1.0)
|
||||
SetEntityVisible(playerPed, 1, 0)
|
||||
SetPlayerInvincible(playerId, 0)
|
||||
FreezeEntityPosition(playerPed, false)
|
||||
Citizen.Wait(10000)
|
||||
canRelog = true
|
||||
end)
|
||||
Citizen.CreateThread(function()
|
||||
local playerPool = {}
|
||||
while hidePlayers do
|
||||
local players = GetActivePlayers()
|
||||
for i=1, #players do
|
||||
local player = players[i]
|
||||
if player ~= PlayerId() and not playerPool[player] then
|
||||
playerPool[player] = true
|
||||
NetworkConcealPlayer(players[player], true, true)
|
||||
end
|
||||
end
|
||||
Citizen.Wait(500)
|
||||
end
|
||||
for i=1, #playerPool do
|
||||
NetworkConcealPlayer(playerPool[i], false, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
SetupCharacter = function(index)
|
||||
if Spawned == false then
|
||||
exports.spawnmanager:spawnPlayer({
|
||||
x = Config.Spawn.x,
|
||||
y = Config.Spawn.y,
|
||||
z = Config.Spawn.z,
|
||||
heading = Config.Spawn.w,
|
||||
model = Characters[index].model or `mp_m_freemode_01`,
|
||||
skipFade = true
|
||||
}, function()
|
||||
canRelog = false
|
||||
if Characters[index] then
|
||||
local skin = Characters[index].skin or Config.Default
|
||||
if not Characters[index].model then
|
||||
if Characters[index].sex == _('female') then skin.sex = 1 else skin.sex = 0 end
|
||||
end
|
||||
TriggerEvent('skinchanger:loadSkin', skin)
|
||||
end
|
||||
DoScreenFadeIn(400)
|
||||
end)
|
||||
repeat Citizen.Wait(200) until not IsScreenFadedOut()
|
||||
|
||||
elseif Characters[index] and Characters[index].skin then
|
||||
if Characters[Spawned] and Characters[Spawned].model then
|
||||
RequestModel(Characters[index].model)
|
||||
while not HasModelLoaded(Characters[index].model) do
|
||||
RequestModel(Characters[index].model)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
SetPlayerModel(PlayerId(), Characters[index].model)
|
||||
SetModelAsNoLongerNeeded(Characters[index].model)
|
||||
end
|
||||
TriggerEvent('skinchanger:loadSkin', Characters[index].skin)
|
||||
end
|
||||
Spawned = index
|
||||
local playerPed = PlayerPedId()
|
||||
FreezeEntityPosition(PlayerPedId(), true)
|
||||
SetPedAoBlobRendering(playerPed, true)
|
||||
SetEntityAlpha(playerPed, 255)
|
||||
SendNUIMessage({
|
||||
action = "openui",
|
||||
character = Characters[Spawned]
|
||||
})
|
||||
end
|
||||
|
||||
RegisterNetEvent('esx_multicharacter:SetupUI')
|
||||
AddEventHandler('esx_multicharacter:SetupUI', function(data)
|
||||
DoScreenFadeOut(0)
|
||||
Characters = data
|
||||
local elements = {}
|
||||
local Character = next(Characters)
|
||||
|
||||
if Character == nil then
|
||||
SendNUIMessage({
|
||||
action = "closeui"
|
||||
})
|
||||
exports.spawnmanager:spawnPlayer({
|
||||
x = Config.Spawn.x,
|
||||
y = Config.Spawn.y,
|
||||
z = Config.Spawn.z,
|
||||
heading = Config.Spawn.w,
|
||||
model = `mp_m_freemode_01`,
|
||||
skipFade = true
|
||||
}, function()
|
||||
canRelog = false
|
||||
DoScreenFadeIn(400)
|
||||
Citizen.Wait(400)
|
||||
local playerPed = PlayerPedId()
|
||||
SetPedAoBlobRendering(playerPed, false)
|
||||
SetEntityAlpha(playerPed, 0)
|
||||
TriggerServerEvent('esx_multicharacter:CharacterChosen', 1, true)
|
||||
TriggerEvent('esx_identity:showRegisterIdentity')
|
||||
end)
|
||||
else
|
||||
for k,v in pairs(Characters) do
|
||||
if not v.model and v.skin then
|
||||
if v.skin.model then v.model = v.skin.model elseif v.skin.sex == 1 then v.model = `mp_f_freemode_01` else v.model = `mp_m_freemode_01` end
|
||||
end
|
||||
if Spawned == false then SetupCharacter(Character) end
|
||||
local label = v.firstname..' '..v.lastname
|
||||
elements[#elements+1] = {label = label, value = v.id}
|
||||
end
|
||||
if #elements < Config.Slots then
|
||||
elements[#elements+1] = {label = _('create_char'), value = (#elements+1), new = true}
|
||||
end
|
||||
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'selectchar', {
|
||||
title = _('select_char'),
|
||||
align = 'top-left',
|
||||
elements = elements
|
||||
}, function(data, menu)
|
||||
local elements = {}
|
||||
if not data.current.new then
|
||||
elements[1] = {label = _('char_play'), action = 'play', value = data.current.value}
|
||||
elements[2] = {label = _('char_delete'), action = 'delete', value = data.current.value}
|
||||
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'choosechar', {
|
||||
title = _('select_char'),
|
||||
align = 'top-left',
|
||||
elements = elements
|
||||
}, function(data, menu)
|
||||
if data.current.action == 'play' then
|
||||
ESX.UI.Menu.CloseAll()
|
||||
SendNUIMessage({
|
||||
action = "closeui"
|
||||
})
|
||||
TriggerServerEvent('esx_multicharacter:CharacterChosen', data.current.value, false)
|
||||
else
|
||||
local elements2 = {}
|
||||
elements2[1] = {label = _('cancel')}
|
||||
elements2[2] = {label = _('confirm'), value = data.current.value}
|
||||
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'deletechar', {
|
||||
title = _('delete_label', Characters[data.current.value].firstname, Characters[data.current.value].lastname),
|
||||
align = 'center',
|
||||
elements = elements2
|
||||
}, function(data, menu)
|
||||
if data.current.value then
|
||||
TriggerServerEvent('esx_multicharacter:DeleteCharacter', data.current.value)
|
||||
Spawned = false
|
||||
ESX.UI.Menu.CloseAll()
|
||||
else
|
||||
menu.close()
|
||||
end
|
||||
end, function(data, menu)
|
||||
menu.close()
|
||||
end)
|
||||
end
|
||||
end, function(data, menu)
|
||||
menu.close()
|
||||
end)
|
||||
else
|
||||
ESX.UI.Menu.CloseAll()
|
||||
local GetSlot = function()
|
||||
for i=1, Config.Slots do
|
||||
if not Characters[i] then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
local slot = GetSlot()
|
||||
TriggerServerEvent('esx_multicharacter:CharacterChosen', slot, true)
|
||||
TriggerEvent('esx_identity:showRegisterIdentity')
|
||||
end
|
||||
end, function(data, menu)
|
||||
menu.refresh()
|
||||
end, function(data, menu)
|
||||
if data.current.new then
|
||||
local playerPed = PlayerPedId()
|
||||
SetPedAoBlobRendering(playerPed, false)
|
||||
SetEntityAlpha(playerPed, 0)
|
||||
SendNUIMessage({
|
||||
action = "closeui"
|
||||
})
|
||||
else
|
||||
SetupCharacter(data.current.value)
|
||||
local playerPed = PlayerPedId()
|
||||
SetPedAoBlobRendering(playerPed, true)
|
||||
ResetEntityAlpha(playerPed)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('esx:playerLoaded')
|
||||
AddEventHandler('esx:playerLoaded', function(playerData, isNew, skin)
|
||||
local spawn = playerData.coords
|
||||
if isNew or not skin or #skin == 1 then
|
||||
local finished = false
|
||||
local sex = skin.sex or 0
|
||||
if sex == 0 then model = `mp_m_freemode_01` else model = `mp_f_freemode_01` end
|
||||
RequestModel(model)
|
||||
while not HasModelLoaded(model) do
|
||||
RequestModel(model)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
SetPlayerModel(PlayerId(), model)
|
||||
SetModelAsNoLongerNeeded(model)
|
||||
skin = Config.Default
|
||||
skin.sex = sex
|
||||
TriggerEvent('skinchanger:loadSkin', skin, function()
|
||||
playerPed = PlayerPedId()
|
||||
SetPedAoBlobRendering(playerPed, true)
|
||||
ResetEntityAlpha(playerPed)
|
||||
TriggerEvent('esx_skin:openSaveableMenu', function()
|
||||
finished = true end, function() finished = true
|
||||
end)
|
||||
end)
|
||||
repeat Citizen.Wait(200) until finished
|
||||
end
|
||||
DoScreenFadeOut(100)
|
||||
repeat Citizen.Wait(200) until IsScreenFadedOut()
|
||||
SetCamActive(cam, false)
|
||||
RenderScriptCams(false, false, 0, true, true)
|
||||
cam = nil
|
||||
local playerPed = PlayerPedId()
|
||||
FreezeEntityPosition(playerPed, true)
|
||||
SetEntityCoords(playerPed, spawn.x, spawn.y, spawn.z-1.3, true, false, false, false)
|
||||
SetEntityHeading(playerPed, spawn.heading)
|
||||
if not isNew then TriggerEvent('skinchanger:loadSkin', skin or Characters[Spawned].skin) end
|
||||
Citizen.Wait(400)
|
||||
DoScreenFadeIn(400)
|
||||
repeat Citizen.Wait(200) until not IsScreenFadedOut()
|
||||
TriggerServerEvent('esx:onPlayerSpawn')
|
||||
TriggerEvent('esx:onPlayerSpawn')
|
||||
TriggerEvent('playerSpawned')
|
||||
TriggerEvent('esx:restoreLoadout')
|
||||
Characters, hidePlayers = {}, false
|
||||
end)
|
||||
|
||||
RegisterNetEvent('esx:onPlayerLogout')
|
||||
AddEventHandler('esx:onPlayerLogout', function()
|
||||
DoScreenFadeOut(0)
|
||||
Spawned = false
|
||||
TriggerEvent("esx_multicharacter:SetupCharacters")
|
||||
TriggerEvent('esx_skin:resetFirstSpawn')
|
||||
end)
|
||||
|
||||
if Config.Relog then
|
||||
RegisterCommand('relog', function(source, args, rawCommand)
|
||||
if canRelog == true then
|
||||
canRelog = false
|
||||
TriggerServerEvent('esx_multicharacter:relog')
|
||||
ESX.SetTimeout(10000, function()
|
||||
canRelog = true
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,110 +0,0 @@
|
||||
Config = {}
|
||||
Config.Locale = 'en'
|
||||
|
||||
Config.Slots = 4
|
||||
Config.Spawn = vector4(-113.7, 565.3, 195.2, 0) -- Sets the location for character selection
|
||||
-- To set the spawn location for new characters, modify the default value in the `users` SQL table
|
||||
|
||||
--------------------
|
||||
-- Do not use unless you are prepared to adjust your resources to correctly reset data
|
||||
-- Information: https://github.com/thelindat/esx_multicharacter#relogging
|
||||
Config.Relog = false
|
||||
--------------------
|
||||
|
||||
Config.Default = {
|
||||
mom = 21,
|
||||
dad = 0,
|
||||
face_md_weight = 50,
|
||||
skin_md_weight = 50,
|
||||
nose_1 = 0,
|
||||
nose_2 = 0,
|
||||
nose_3 = 0,
|
||||
nose_4 = 0,
|
||||
nose_5 = 0,
|
||||
nose_6 = 0,
|
||||
cheeks_1 = 0,
|
||||
cheeks_2 = 0,
|
||||
cheeks_3 = 0,
|
||||
lip_thickness = 0,
|
||||
jaw_1 = 0,
|
||||
jaw_2 = 0,
|
||||
chin_1 = 0,
|
||||
chin_2 = 0,
|
||||
chin_13 = 0,
|
||||
chin_4 = 0,
|
||||
neck_thickness = 0,
|
||||
hair_1 = 0,
|
||||
hair_2 = 0,
|
||||
hair_color_1 = 0,
|
||||
hair_color_2 = 0,
|
||||
tshirt_1 = 0,
|
||||
tshirt_2 = 0,
|
||||
torso_1 = 0,
|
||||
torso_2 = 0,
|
||||
decals_1 = 0,
|
||||
decals_2 = 0,
|
||||
arms = 0,
|
||||
arms_2 = 0,
|
||||
pants_1 = 0,
|
||||
pants_2 = 0,
|
||||
shoes_1 = 0,
|
||||
shoes_2 = 0,
|
||||
mask_1 = 0,
|
||||
mask_2 = 0,
|
||||
bproof_1 = 0,
|
||||
bproof_2 = 0,
|
||||
chain_1 = 0,
|
||||
chain_2 = 0,
|
||||
helmet_1 = -1,
|
||||
helmet_2 = 0,
|
||||
glasses_1 = 0,
|
||||
glasses_2 = 0,
|
||||
watches_1 = -1,
|
||||
watches_2 = 0,
|
||||
bracelets_1 = -1,
|
||||
bracelets_2 = 0,
|
||||
bags_1 = 0,
|
||||
bags_2 = 0,
|
||||
eye_color = 0,
|
||||
eye_squint = 0,
|
||||
eyebrows_2 = 0,
|
||||
eyebrows_1 = 0,
|
||||
eyebrows_3 = 0,
|
||||
eyebrows_4 = 0,
|
||||
eyebrows_5 = 0,
|
||||
eyebrows_6 = 0,
|
||||
makeup_1 = 0,
|
||||
makeup_2 = 0,
|
||||
makeup_3 = 0,
|
||||
makeup_4 = 0,
|
||||
lipstick_1 = 0,
|
||||
lipstick_2 = 0,
|
||||
lipstick_3 = 0,
|
||||
lipstick_4 = 0,
|
||||
ears_1 = -1,
|
||||
ears_2 = 0,
|
||||
chest_1 = 0,
|
||||
chest_2 = 0,
|
||||
chest_3 = 0,
|
||||
bodyb_1 = -1,
|
||||
bodyb_2 = 0,
|
||||
bodyb_3 = -1,
|
||||
bodyb_4 = 0,
|
||||
age_1 = 0,
|
||||
age_2 = 0,
|
||||
blemishes_1 = 0,
|
||||
blemishes_2 = 0,
|
||||
blush_1 = 0,
|
||||
blush_2 = 0,
|
||||
blush_3 = 0,
|
||||
complexion_1 = 0,
|
||||
complexion_2 = 0,
|
||||
sun_1 = 0,
|
||||
sun_2 = 0,
|
||||
moles_1 = 0,
|
||||
moles_2 = 0,
|
||||
beard_1 = 0,
|
||||
beard_2 = 0,
|
||||
beard_3 = 0,
|
||||
beard_4 = 0
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
fx_version 'adamant'
|
||||
game 'gta5'
|
||||
description 'https://github.com/thelindat/esx_multicharacter'
|
||||
version '1.2.2'
|
||||
|
||||
dependencies {
|
||||
'es_extended',
|
||||
'esx_menu_default',
|
||||
'esx_identity',
|
||||
'esx_skin'
|
||||
}
|
||||
|
||||
shared_scripts {
|
||||
'@es_extended/locale.lua',
|
||||
'locales/*.lua',
|
||||
'config.lua'
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'@es_extended/imports.lua',
|
||||
'@mysql-async/lib/MySQL.lua',
|
||||
'server/*.lua'
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
'client/*.lua'
|
||||
}
|
||||
|
||||
ui_page {
|
||||
'html/ui.html',
|
||||
}
|
||||
|
||||
files {
|
||||
'html/ui.html',
|
||||
'html/css/main.css',
|
||||
'html/js/app.js',
|
||||
'html/locales/*.js',
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@300;600&display=swap');
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
user-select: none;
|
||||
color: rgb(255, 255, 255);
|
||||
font-weight: 300;
|
||||
font-size: 1.6vh;
|
||||
font-family: Calibri, "Helvetica", san-serif;
|
||||
}
|
||||
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
body {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
display:none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
transform: translate(0, -50%);
|
||||
background: rgba(0,0,0,0.7);
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
top: 5%;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 5%;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.character-box {
|
||||
display: flex;
|
||||
right: 0;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
line-height: 1.4rem;
|
||||
height: calc(100vh - 6rem);
|
||||
width: 20rem;
|
||||
padding: 1rem;
|
||||
border: 1px rgba(12,12,12,200) solid;
|
||||
box-shadow: 0px 0px 4px 1px rgba(12,12,12,20);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Raleway', sans-serif;
|
||||
padding-top: 2rem;
|
||||
display: block;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
var money = Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 0
|
||||
});
|
||||
|
||||
(() => {
|
||||
Kashacter = {};
|
||||
|
||||
Kashacter.ShowUI = function(data) {
|
||||
$('body').css({"display":"block"});
|
||||
$('.main-container').css({"display":"block"});
|
||||
$('[data-charid=1]').html('<div class="character-info"><p class="character-info-name"><h1>' + `${translate.name} ` + '</h1><span>' + data.firstname +' '+ data.lastname +'</span></p><p class="character-info-work"><h1>' + `${translate.job} ` + '</h1><span>'+ data.job +' '+ data.job_grade +'</span></p><p class="character-info-money"><h1>' + `${translate.money} ` + '</h1><span> '+ money.format(data.money) +'</span></p><p class="character-info-bank"><h1>' + `${translate.bank} ` + '</h1><span> '+ money.format(data.bank) +'</span></p> <p class="character-info-dateofbirth"><h1>' + `${translate.dob} ` + '</h1><span>'+ data.dateofbirth +'</span></p> <p class="character-info-gender"><h1>' + `${translate.gender} ` + '</h1><span>'+ data.sex +'</span></p></div>').attr("data-ischar", "true");
|
||||
};
|
||||
|
||||
Kashacter.CloseUI = function() {
|
||||
$('body').css({"display":"none"});
|
||||
$('.main-container').css({"display":"none"});
|
||||
$('[data-charid=1]').html('<h3 class="character-fullname"></h3><div class="character-info"><p class="character-info-new"></p></div>');
|
||||
};
|
||||
|
||||
window.onload = function(e) {
|
||||
window.addEventListener('message', function(event) {
|
||||
switch(event.data.action) {
|
||||
case 'openui':
|
||||
Kashacter.ShowUI(event.data.character);
|
||||
break;
|
||||
case 'closeui':
|
||||
Kashacter.CloseUI();
|
||||
break;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})();
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "Jméno";
|
||||
translate.job = "Práce";
|
||||
translate.bank = "Banka";
|
||||
translate.money = "Peníze";
|
||||
translate.gender = "Pohlaví";
|
||||
translate.dob = "Datum narození";
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "Name";
|
||||
translate.job = "Beruf";
|
||||
translate.bank = "Bankguthaben";
|
||||
translate.money = "Bargeld";
|
||||
translate.gender = "Geschlecht";
|
||||
translate.dob = "Geburtsdatum";
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "Name";
|
||||
translate.job = "Job";
|
||||
translate.bank = "Bank";
|
||||
translate.money = "Cash";
|
||||
translate.gender = "Gender";
|
||||
translate.dob = "Date of birth";
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "Nom";
|
||||
translate.job = "Métier";
|
||||
translate.bank = "Banque";
|
||||
translate.money = "Argent";
|
||||
translate.gender = "Sexe";
|
||||
translate.dob = "Date de naissance";
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "imię";
|
||||
translate.job = "Zajęcie";
|
||||
translate.bank = "Bank";
|
||||
translate.money = "Gotówka";
|
||||
translate.gender = "Seks";
|
||||
translate.dob = "Data urodzenia";
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "Nome";
|
||||
translate.job = "Trabalho";
|
||||
translate.bank = "Banco";
|
||||
translate.money = "Dinheiro";
|
||||
translate.gender = "Género";
|
||||
translate.dob = "Data de Nascimento";
|
||||
@@ -1,8 +0,0 @@
|
||||
const translate = new Object();
|
||||
|
||||
translate.name = "İsim";
|
||||
translate.job = "Meslek";
|
||||
translate.bank = "Banka";
|
||||
translate.money = "Nakit";
|
||||
translate.gender = "Cinsiyet";
|
||||
translate.dob = "Doğum Tarihi";
|
||||
@@ -1,24 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="css/main.css" />
|
||||
<!-- Locales -->
|
||||
<script src="locales/en.js"></script>
|
||||
</head>
|
||||
<body style="display: none;">
|
||||
<div class="main-container">
|
||||
<div class='header'>ESX Legacy</div>
|
||||
<div class="character-box" data-charid="1">
|
||||
<h3 class="character-fullname"></h3>
|
||||
<div class="character-info"></div>
|
||||
</div>
|
||||
<div class='footer'>Multicharacter</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
|
||||
<script src="js/app.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
Locales['cs'] = {
|
||||
['male'] = "Muž",
|
||||
['female'] = "Žena",
|
||||
['delete_label'] = "Vymazat %s %s?",
|
||||
['select_char'] = "Zvolit Postavu",
|
||||
['create_char'] = "Vytvořit novou postavu",
|
||||
['char_play'] = "Hrát za postavu",
|
||||
['char_delete'] = "Vymazat postavu",
|
||||
['cancel'] = "Zrušit",
|
||||
['confirm'] = "Potvrdit",
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
Locales['de'] = {
|
||||
['male'] = "Männlich",
|
||||
['female'] = "Weiblich",
|
||||
['delete_label'] = "Lösche %s %s?",
|
||||
['select_char'] = "Charakter auswählen",
|
||||
['create_char'] = "Neuen Charakter erstellen",
|
||||
['char_play'] = "Diesen Charakter spielen",
|
||||
['char_delete'] = "Diesen Charakter löschen",
|
||||
['cancel'] = "Abbrechen",
|
||||
['confirm'] = "Bestätigen",
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
Locales['en'] = {
|
||||
['male'] = "Male",
|
||||
['female'] = "Female",
|
||||
['delete_label'] = "Delete %s %s?",
|
||||
['select_char'] = "Select Character",
|
||||
['create_char'] = "Create new character",
|
||||
['char_play'] = "Play this character",
|
||||
['char_delete'] = "Delete this character",
|
||||
['cancel'] = "Cancel",
|
||||
['confirm'] = "Confirm",
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
Locales['pt'] = {
|
||||
['male'] = "Masculino",
|
||||
['female'] = "Feminino",
|
||||
['delete_label'] = "Eliminar %s %s?",
|
||||
['select_char'] = "Selecionar personagem",
|
||||
['create_char'] = "Criar novo personagem",
|
||||
['char_play'] = "Selecionar",
|
||||
['char_delete'] = "Eliminar personagem",
|
||||
['cancel'] = "Cancelar",
|
||||
['confirm'] = "Confirmar",
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
Locales['tr'] = {
|
||||
['male'] = "Erkek",
|
||||
['female'] = "Kadın",
|
||||
['delete_label'] = "Sil %s %s?",
|
||||
['select_char'] = "Karakter Seç",
|
||||
['create_char'] = "Yeni Karakter Oluştur",
|
||||
['char_play'] = "Bu Karakterle Oyna",
|
||||
['char_delete'] = "Bu Karakteri Sil",
|
||||
['cancel'] = "İptal",
|
||||
['confirm'] = "Onayla",
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
### Requirements (ensure you are using the latest)
|
||||
- [ESX Legacy](https://github.com/esx-framework/esx-legacy)
|
||||
- [MySQL Async 3.3.2](https://github.com/brouznouf/fivem-mysql-async/releases/tag/3.3.2)
|
||||
- [ESX Identity](https://github.com/esx-framework/esx_identity)
|
||||
- [ESX Skin](https://github.com/esx-framework/esx_skin)
|
||||
- [Spawnmanager](https://github.com/citizenfx/cfx-server-data/tree/master/resources/%5Bmanagers%5D/spawnmanager)
|
||||
|
||||
### Installation
|
||||
- Modify your ESX config with `Config.Multichar = true`
|
||||
- Set your database name for `Config.Database` in server/main.lua
|
||||
- All owner and identifier columns should be set to `VARCHAR(60)` to ensure correct data entry
|
||||
- The resource will attempt to set columns automatically
|
||||
|
||||
### Conflicts
|
||||
* The following resources should not be used with ESX Legacy and can result in errors
|
||||
- essentialmode
|
||||
- basic-gamemode
|
||||
- fivem-map-skater
|
||||
- fivem-map-hipster
|
||||
- default_spawnpoint
|
||||
|
||||
### Common issues
|
||||
#### Black screen / loading scripts
|
||||
- Download and run all requirements
|
||||
- Use a fresh spawnmanager as many people alter the code
|
||||
- Ensure none of the conflicting resources are enabled
|
||||
#### mysql-async duplicate entry
|
||||
- You have not increased the VARCHAR size of the table holding identifiers - usually `owner` or `identifier`
|
||||
|
||||
#### The menu interface is esx_menu_default - you can use any version if you want a different appearance
|
||||

|
||||
|
||||
### Relogging
|
||||
- Do not enable this setting if you do not intend to properly set up relog support
|
||||
- Requires the latest update for ESX Status (prevents multiple status ticks from running)
|
||||
- Add the following events to resources that require support for relogging, or
|
||||
- Add them to [@esx/imports.lua](https://github.com/esx-framework/es_extended/blob/legacy/imports.lua) (and use the imports in your resources)
|
||||
```lua
|
||||
RegisterNetEvent('esx:playerLoaded')
|
||||
AddEventHandler('esx:playerLoaded', function(xPlayer)
|
||||
ESX.PlayerData = xPlayer
|
||||
ESX.PlayerLoaded = true
|
||||
end)
|
||||
|
||||
RegisterNetEvent('esx:onPlayerLogout')
|
||||
AddEventHandler('esx:onPlayerLogout', function()
|
||||
ESX.PlayerLoaded = false
|
||||
ESX.PlayerData = {}
|
||||
end)
|
||||
```
|
||||
- Any threads using ESX.PlayerData in a loop should check if ESX.PlayerLoaded is true
|
||||
- This ensures the resource does not error after relogging, or while on character selection
|
||||
- Setup correctly you can break your loops and trigger them again after loading
|
||||
- Refer to my [boilerplate](https://github.com/thelindat/esx_legacy_boilerplate) for more information and usage examples
|
||||
|
||||
### Notes
|
||||
- This resource is not compatible with ExtendedMode or previous versions of ESX
|
||||
- Legacy, skin, and identity must be updated to at least the minimum commits specified above
|
||||
- Characters are stored in the users table as `char#:license` - if you need to use a different identifier then you need to modify ESX itself
|
||||
- Character deletion does not require manual entries for the tables to remove
|
||||
- As characters are stored with unique identifiers, there is no excessive queries being executed
|
||||
|
||||
### Kashacters
|
||||
- This project is forked from the [kashacters multicharacter resource](https://github.com/FiveEYZ/esx_kashacter)
|
||||
- Most of the code has been entirely rewritten
|
||||
- KASH has given permission for this resource to use his code and the addition of a license
|
||||
- The license obviously does not apply to previous versions and KASH has stated his resource is free to be used however
|
||||
|
||||
|
||||
|
||||
## Notice
|
||||
Copyright© 2021 Linden and KASH
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see https://www.gnu.org/licenses.
|
||||
|
||||
|
||||
### Thanks to KASH, XxFri3ndlyxX, and all those who have contributed
|
||||
@@ -1,8 +0,0 @@
|
||||
GetJobs = function()
|
||||
local jobs = {}
|
||||
while next(jobs) == nil do
|
||||
Citizen.Wait(250)
|
||||
jobs = exports['es_extended']:getSharedObject().Jobs
|
||||
end
|
||||
return jobs
|
||||
end
|
||||
@@ -1,132 +0,0 @@
|
||||
if not ESX then
|
||||
SetTimeout(3000, function() print('[^3WARNING^7] Unable to start Multicharacter - your version of ESX is not compatible ') end)
|
||||
elseif ESX.GetConfig().Multichar == true then
|
||||
local IdentifierTables, Fetch = {}, -1
|
||||
|
||||
-- enter the name of your database here
|
||||
Config.Database = 'es_extended'
|
||||
|
||||
-- enter a prefix to prepend to all user identifiers (keep it short)
|
||||
Config.Prefix = 'char'
|
||||
|
||||
local SetupCharacters = function(playerId)
|
||||
while Fetch == -1 do Citizen.Wait(500) end
|
||||
local identifier = Config.Prefix..'%:'..ESX.GetIdentifier(playerId)
|
||||
MySQL.Async.fetchAll(Fetch, {
|
||||
identifier,
|
||||
Config.Slots
|
||||
}, function(result)
|
||||
local characters = {}
|
||||
for i=1, #result, 1 do
|
||||
local job, grade = result[i].job or 'unemployed', tostring(result[i].job_grade)
|
||||
if ESX.Jobs[job] and ESX.Jobs[job].grades[grade] then
|
||||
if job ~= 'unemployed' then grade = ESX.Jobs[job].grades[grade].label else grade = '' end
|
||||
job = ESX.Jobs[job].label
|
||||
end
|
||||
local accounts = json.decode(result[i].accounts)
|
||||
local id = tonumber(string.sub(result[i].identifier, #Config.Prefix+1, string.find(result[i].identifier, ':')-1))
|
||||
characters[id] = {
|
||||
id = id,
|
||||
bank = accounts.bank,
|
||||
money = accounts.money,
|
||||
job = job,
|
||||
job_grade = grade,
|
||||
firstname = result[i].firstname,
|
||||
lastname = result[i].lastname,
|
||||
dateofbirth = result[i].dateofbirth,
|
||||
skin = json.decode(result[i].skin)
|
||||
}
|
||||
if result[i].sex == 'm' then characters[id].sex = _('male') else characters[id].sex = _('female') end
|
||||
end
|
||||
TriggerClientEvent('esx_multicharacter:SetupUI', playerId, characters)
|
||||
end)
|
||||
end
|
||||
|
||||
local DeleteCharacter = function(playerId, charid)
|
||||
local identifier = Config.Prefix..charid..':'..ESX.GetIdentifier(playerId)
|
||||
local counter = 0
|
||||
for k, v in pairs(IdentifierTables) do
|
||||
MySQL.Async.execute("DELETE FROM "..v.table.." WHERE "..v.column.." = ?", {
|
||||
identifier
|
||||
}, function()
|
||||
counter = counter + 1
|
||||
if counter == #IdentifierTables then
|
||||
print(('[^2INFO^7] Player [%s] %s has deleted a character (%s)'):format(GetPlayerName(playerId), playerId, identifier))
|
||||
Citizen.Wait(100)
|
||||
SetupCharacters(playerId)
|
||||
end
|
||||
end)
|
||||
Citizen.Wait(5)
|
||||
end
|
||||
end
|
||||
|
||||
MySQL.ready(function()
|
||||
MySQL.Async.fetchAll('SELECT `TABLE_NAME`, `COLUMN_NAME`, `CHARACTER_MAXIMUM_LENGTH` FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND (COLUMN_NAME = ? OR COLUMN_NAME = ?) ', {
|
||||
Config.Database, 'identifier', 'owner'
|
||||
}, function(result)
|
||||
if result then
|
||||
local varchar, varsize = {}, 0
|
||||
for k, v in pairs(result) do
|
||||
if v.CHARACTER_MAXIMUM_LENGTH and v.CHARACTER_MAXIMUM_LENGTH >= 40 and v.CHARACTER_MAXIMUM_LENGTH < 60 then varchar[v.TABLE_NAME] = v.COLUMN_NAME varsize = varsize+1 end
|
||||
table.insert(IdentifierTables, {table = v.TABLE_NAME, column = v.COLUMN_NAME})
|
||||
end
|
||||
if next(varchar) then
|
||||
for k, v in pairs(varchar) do
|
||||
MySQL.Sync.execute("ALTER TABLE "..k.." MODIFY COLUMN "..v.." VARCHAR(60)")
|
||||
end
|
||||
print(('[^2INFO^7] Attempted to update ^5%s^7 columns to use VARCHAR(60)'):format(varsize))
|
||||
end
|
||||
if not next(ESX.Jobs) then ESX.Jobs = GetJobs() end
|
||||
Fetch = MySQL.Sync.store("SELECT `identifier`, `accounts`, `job`, `job_grade`, `firstname`, `lastname`, `dateofbirth`, `sex`, `skin` FROM `users` WHERE identifier LIKE ? LIMIT ?")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterServerEvent("esx_multicharacter:SetupCharacters")
|
||||
AddEventHandler('esx_multicharacter:SetupCharacters', function()
|
||||
SetupCharacters(source)
|
||||
end)
|
||||
|
||||
local awaitingRegistration = {}
|
||||
RegisterServerEvent("esx_multicharacter:CharacterChosen")
|
||||
AddEventHandler('esx_multicharacter:CharacterChosen', function(charid, isNew)
|
||||
local src = source
|
||||
if type(charid) == 'number' and string.len(charid) <= 2 and type(isNew) == 'boolean' then
|
||||
if isNew then
|
||||
awaitingRegistration[src] = charid
|
||||
else
|
||||
TriggerEvent('esx:onPlayerJoined', src, Config.Prefix..charid)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_identity:completedRegistration', function(playerId, data)
|
||||
TriggerEvent('esx:onPlayerJoined', playerId, Config.Prefix..awaitingRegistration[playerId], data)
|
||||
awaitingRegistration[playerId] = nil
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function(reason)
|
||||
awaitingRegistration[source] = nil
|
||||
end)
|
||||
|
||||
RegisterServerEvent("esx_multicharacter:DeleteCharacter")
|
||||
AddEventHandler('esx_multicharacter:DeleteCharacter', function(charid)
|
||||
local src = source
|
||||
if type(charid) == "number" and string.len(charid) <= 2 then
|
||||
DeleteCharacter(src, charid)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterServerEvent("esx_multicharacter:relog")
|
||||
AddEventHandler('esx_multicharacter:relog', function()
|
||||
local src = source
|
||||
TriggerEvent('esx:playerLogout', src)
|
||||
end)
|
||||
|
||||
RegisterCommand('forcelog', function(source, args, rawCommand)
|
||||
TriggerEvent('esx:playerLogout', source)
|
||||
end, true)
|
||||
|
||||
else
|
||||
SetTimeout(3000, function() print('[^3WARNING^7] Multicharacter is disabled - please check your ESX configuration') end)
|
||||
end
|
||||
@@ -22,7 +22,7 @@ function LoadPlayer(player)
|
||||
end
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE identifier = @identifier', {
|
||||
MySQL.query('SELECT phone_number FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
local phoneNumber = result[1].phone_number
|
||||
@@ -30,7 +30,7 @@ function LoadPlayer(player)
|
||||
if phoneNumber == nil then
|
||||
phoneNumber = GenerateUniquePhoneNumber()
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', {
|
||||
MySQL.update('UPDATE users SET phone_number = @phone_number WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@phone_number'] = phoneNumber
|
||||
})
|
||||
@@ -53,7 +53,7 @@ function LoadPlayer(player)
|
||||
TriggerEvent('esx_phone:addSource', xPlayer.job.name, xPlayer.source)
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', {
|
||||
MySQL.query('SELECT * FROM user_contacts WHERE identifier = @identifier ORDER BY name ASC', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result2)
|
||||
for i=1, #result2, 1 do
|
||||
@@ -78,7 +78,7 @@ function GenerateUniquePhoneNumber()
|
||||
math.randomseed(GetGameTimer())
|
||||
phoneNumber = math.random(10000, 99999)
|
||||
|
||||
local result = MySQL.Sync.fetchAll('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', {
|
||||
local result = MySQL.query.await('SELECT COUNT(*) as count FROM users WHERE phone_number = @phoneNumber', {
|
||||
['@phoneNumber'] = phoneNumber
|
||||
})
|
||||
|
||||
@@ -206,7 +206,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName)
|
||||
return
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll('SELECT phone_number, identifier FROM users WHERE phone_number = @number', {
|
||||
MySQL.query('SELECT phone_number, identifier FROM users WHERE phone_number = @number', {
|
||||
['@number'] = phoneNumber
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
@@ -234,7 +234,7 @@ AddEventHandler('esx_phone:addPlayerContact', function(phoneNumber, contactName)
|
||||
local xTarget = ESX.GetPlayerFromIdentifier(result[1].identifier)
|
||||
playerOnline = xTarget ~= nil
|
||||
|
||||
MySQL.Async.execute('INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', {
|
||||
MySQL.update('INSERT INTO user_contacts (identifier, name, number) VALUES (@identifier, @name, @number)', {
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@name'] = contactName,
|
||||
['@number'] = phoneNumber
|
||||
@@ -255,7 +255,7 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa
|
||||
local xPlayer = ESX.GetPlayerFromId(playerId)
|
||||
local foundNumber = false
|
||||
|
||||
MySQL.Async.fetchAll('SELECT phone_number FROM users WHERE phone_number = @number', {
|
||||
MySQL.query('SELECT phone_number FROM users WHERE phone_number = @number', {
|
||||
['@number'] = phoneNumber
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
@@ -273,7 +273,7 @@ AddEventHandler('esx_phone:removePlayerContact', function(phoneNumber, contactNa
|
||||
|
||||
xPlayer.set('contacts', contacts)
|
||||
|
||||
MySQL.Async.execute('DELETE FROM user_contacts WHERE identifier = @identifier AND name = @name AND number = @number', {
|
||||
MySQL.update('DELETE FROM user_contacts WHERE identifier = @identifier AND name = @name AND number = @number', {
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@name'] = contactName,
|
||||
['@number'] = phoneNumber
|
||||
|
||||
@@ -118,17 +118,17 @@ function OpenVehicleSpawnerMenu(type, station, part, partNum)
|
||||
end
|
||||
|
||||
function StoreNearbyVehicle(playerCoords)
|
||||
local vehicles, vehiclePlates = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}
|
||||
|
||||
if #vehicles > 0 then
|
||||
for k,v in ipairs(vehicles) do
|
||||
local vehicles, plates, index = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}, {}
|
||||
|
||||
if next(vehicles) then
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
|
||||
-- Make sure the vehicle we're saving is empty, or else it wont be deleted
|
||||
if GetVehicleNumberOfPassengers(v) == 0 and IsVehicleSeatFree(v, -1) then
|
||||
table.insert(vehiclePlates, {
|
||||
vehicle = v,
|
||||
plate = ESX.Math.Trim(GetVehicleNumberPlateText(v))
|
||||
})
|
||||
if GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1) then
|
||||
local plate = ESX.Math.Trim(GetVehicleNumberPlateText(vehicle))
|
||||
plates[#plates + 1] = plate
|
||||
index[plate] = vehicle
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -136,27 +136,22 @@ function StoreNearbyVehicle(playerCoords)
|
||||
return
|
||||
end
|
||||
|
||||
ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(storeSuccess, foundNum)
|
||||
if storeSuccess then
|
||||
local vehicleId = vehiclePlates[foundNum]
|
||||
ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(plate)
|
||||
if plate then
|
||||
local vehicleId = index[plate]
|
||||
local attempts = 0
|
||||
ESX.Game.DeleteVehicle(vehicleId.vehicle)
|
||||
IsBusy = true
|
||||
ESX.Game.DeleteVehicle(vehicleId)
|
||||
local isBusy = true
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
BeginTextCommandBusyspinnerOn('STRING')
|
||||
AddTextComponentSubstringPlayerName(_U('garage_storing'))
|
||||
EndTextCommandBusyspinnerOn(4)
|
||||
|
||||
while IsBusy do
|
||||
Citizen.Wait(100)
|
||||
while isBusy do
|
||||
Citizen.Wait(0)
|
||||
drawLoadingText(_U('garage_storing'), 255, 255, 255, 255)
|
||||
end
|
||||
|
||||
BusyspinnerOff()
|
||||
end)
|
||||
|
||||
-- Workaround for vehicle not deleting when other players are near it.
|
||||
while DoesEntityExist(vehicleId.vehicle) do
|
||||
while DoesEntityExist(vehicleId) do
|
||||
Citizen.Wait(500)
|
||||
attempts = attempts + 1
|
||||
|
||||
@@ -167,21 +162,22 @@ function StoreNearbyVehicle(playerCoords)
|
||||
|
||||
vehicles = ESX.Game.GetVehiclesInArea(playerCoords, 30.0)
|
||||
if #vehicles > 0 then
|
||||
for k,v in ipairs(vehicles) do
|
||||
if ESX.Math.Trim(GetVehicleNumberPlateText(v)) == vehicleId.plate then
|
||||
ESX.Game.DeleteVehicle(v)
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
if ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)) == plate then
|
||||
ESX.Game.DeleteVehicle(vehicle)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
IsBusy = false
|
||||
isBusy = false
|
||||
ESX.ShowNotification(_U('garage_has_stored'))
|
||||
else
|
||||
ESX.ShowNotification(_U('garage_has_notstored'))
|
||||
end
|
||||
end, vehiclePlates)
|
||||
end, plates)
|
||||
end
|
||||
|
||||
function GetAvailableVehicleSpawnPoint(station, part, partNum)
|
||||
|
||||
@@ -57,7 +57,7 @@ AddEventHandler('esx_policejob:confiscatePlayerItem', function(target, itemType,
|
||||
|
||||
-- does the target player have weapon?
|
||||
if targetXPlayer.hasWeapon(itemName) then
|
||||
targetXPlayer.removeWeapon(itemName, amount)
|
||||
targetXPlayer.removeWeapon(itemName)
|
||||
sourceXPlayer.addWeapon (itemName, amount)
|
||||
|
||||
sourceXPlayer.showNotification(_U('you_confiscated_weapon', ESX.GetWeaponLabel(itemName), targetXPlayer.name, amount))
|
||||
@@ -198,44 +198,36 @@ ESX.RegisterServerCallback('esx_policejob:getOtherPlayerData', function(source,
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_policejob:getFineList', function(source, cb, category)
|
||||
MySQL.Async.fetchAll('SELECT * FROM fine_types WHERE category = @category', {
|
||||
['@category'] = category
|
||||
}, function(fines)
|
||||
MySQL.query('SELECT * FROM fine_types WHERE category = ?', {category},
|
||||
function(fines)
|
||||
cb(fines)
|
||||
end)
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_policejob:getVehicleInfos', function(source, cb, plate)
|
||||
MySQL.Async.fetchAll('SELECT owner FROM owned_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = plate
|
||||
}, function(result)
|
||||
local retrivedInfo = {plate = plate}
|
||||
|
||||
if result[1] then
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(result[1].owner)
|
||||
|
||||
-- is the owner online?
|
||||
if xPlayer then
|
||||
retrivedInfo.owner = xPlayer.getName()
|
||||
cb(retrivedInfo)
|
||||
elseif Config.EnableESXIdentity then
|
||||
MySQL.Async.fetchAll('SELECT firstname, lastname FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = result[1].owner
|
||||
}, function(result2)
|
||||
if result2[1] then
|
||||
retrivedInfo.owner = ('%s %s'):format(result2[1].firstname, result2[1].lastname)
|
||||
cb(retrivedInfo)
|
||||
else
|
||||
cb(retrivedInfo)
|
||||
end
|
||||
end)
|
||||
else
|
||||
cb(retrivedInfo)
|
||||
local retrivedInfo = {
|
||||
plate = plate
|
||||
}
|
||||
if Config.EnableESXIdentity then
|
||||
MySQL.single('SELECT users.firstname, users.lastname FROM owned_vehicles JOIN users ON owned_vehicles.owner = users.identifier WHERE plate = ?', {plate},
|
||||
function(result)
|
||||
if result then
|
||||
retrivedInfo.owner = ('%s %s'):format(result.firstname, result.lastname)
|
||||
end
|
||||
else
|
||||
cb(retrivedInfo)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
else
|
||||
MySQL.scalar('SELECT owner FROM owned_vehicles WHERE plate = ?', {plate},
|
||||
function(owner)
|
||||
if owner then
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(owner)
|
||||
if xPlayer then
|
||||
retrivedInfo.owner = xPlayer.getName()
|
||||
end
|
||||
end
|
||||
cb(retrivedInfo)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_policejob:getArmoryWeapons', function(source, cb)
|
||||
@@ -278,7 +270,7 @@ ESX.RegisterServerCallback('esx_policejob:addArmoryWeapon', function(source, cb,
|
||||
|
||||
store.set('weapons', weapons)
|
||||
cb()
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_policejob:removeArmoryWeapon', function(source, cb, weaponName)
|
||||
@@ -371,14 +363,8 @@ ESX.RegisterServerCallback('esx_policejob:buyJobVehicle', function(source, cb, v
|
||||
if xPlayer.getMoney() >= price then
|
||||
xPlayer.removeMoney(price)
|
||||
|
||||
MySQL.Async.execute('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (@owner, @vehicle, @plate, @type, @job, @stored)', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@vehicle'] = json.encode(vehicleProps),
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@type'] = type,
|
||||
['@job'] = xPlayer.job.name,
|
||||
['@stored'] = true
|
||||
}, function (rowsChanged)
|
||||
MySQL.insert('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (?, ?, ?, ?, ?, ?)', { xPlayer.identifier, json.encode(vehicleProps), vehicleProps.plate, type, xPlayer.job.name, true},
|
||||
function (rowsChanged)
|
||||
cb(true)
|
||||
end)
|
||||
else
|
||||
@@ -387,47 +373,32 @@ ESX.RegisterServerCallback('esx_policejob:buyJobVehicle', function(source, cb, v
|
||||
end
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_policejob:storeNearbyVehicle', function(source, cb, nearbyVehicles)
|
||||
ESX.RegisterServerCallback('esx_policejob:storeNearbyVehicle', function(source, cb, plates)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
local foundPlate, foundNum
|
||||
|
||||
for k,v in ipairs(nearbyVehicles) do
|
||||
local result = MySQL.Sync.fetchAll('SELECT plate FROM owned_vehicles WHERE owner = @owner AND plate = @plate AND job = @job', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = v.plate,
|
||||
['@job'] = xPlayer.job.name
|
||||
})
|
||||
local plate = MySQL.scalar.await('SELECT plate FROM owned_vehicles WHERE owner = ? AND plate IN (?) AND job = ?', {xPlayer.identifier, plates, xPlayer.job.name})
|
||||
|
||||
if result[1] then
|
||||
foundPlate, foundNum = result[1].plate, k
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not foundPlate then
|
||||
cb(false)
|
||||
else
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = true WHERE owner = @owner AND plate = @plate AND job = @job', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = foundPlate,
|
||||
['@job'] = xPlayer.job.name
|
||||
}, function (rowsChanged)
|
||||
if plate then
|
||||
MySQL.update('UPDATE owned_vehicles SET `stored` = true WHERE owner = ? AND plate = ? AND job = ?', {xPlayer.identifier, plate, xPlayer.job.name},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 0 then
|
||||
print(('esx_policejob: %s has exploited the garage!'):format(xPlayer.identifier))
|
||||
cb(false)
|
||||
else
|
||||
cb(true, foundNum)
|
||||
cb(plate)
|
||||
end
|
||||
end)
|
||||
else
|
||||
cb(false)
|
||||
end
|
||||
end)
|
||||
|
||||
function getPriceFromHash(vehicleHash, jobGrade, type)
|
||||
local vehicles = Config.AuthorizedVehicles[type][jobGrade]
|
||||
|
||||
for k,v in ipairs(vehicles) do
|
||||
if GetHashKey(v.model) == vehicleHash then
|
||||
return v.price
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
if GetHashKey(vehicle.model) == vehicleHash then
|
||||
return vehicle.price
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ function GetProperty(name)
|
||||
end
|
||||
|
||||
function SetPropertyOwned(name, price, rented, owner)
|
||||
MySQL.Async.execute('INSERT INTO owned_properties (name, price, rented, owner) VALUES (@name, @price, @rented, @owner)', {
|
||||
MySQL.update('INSERT INTO owned_properties (name, price, rented, owner) VALUES (@name, @price, @rented, @owner)', {
|
||||
['@name'] = name,
|
||||
['@price'] = price,
|
||||
['@rented'] = (rented and 1 or 0),
|
||||
@@ -28,12 +28,12 @@ function SetPropertyOwned(name, price, rented, owner)
|
||||
end
|
||||
|
||||
function RemoveOwnedProperty(name, owner, noPay)
|
||||
MySQL.Async.fetchAll('SELECT id, rented, price FROM owned_properties WHERE name = @name AND owner = @owner', {
|
||||
MySQL.query('SELECT id, rented, price FROM owned_properties WHERE name = @name AND owner = @owner', {
|
||||
['@name'] = name,
|
||||
['@owner'] = owner
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
MySQL.Async.execute('DELETE FROM owned_properties WHERE id = @id', {
|
||||
MySQL.update('DELETE FROM owned_properties WHERE id = @id', {
|
||||
['@id'] = result[1].id
|
||||
}, function(rowsChanged)
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(owner)
|
||||
@@ -60,7 +60,7 @@ end
|
||||
MySQL.ready(function()
|
||||
Citizen.Wait(1500)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM `properties`', {}, function(properties)
|
||||
MySQL.query('SELECT * FROM `properties`', {}, function(properties)
|
||||
|
||||
for i=1, #properties, 1 do
|
||||
local entering = nil
|
||||
@@ -136,7 +136,7 @@ ESX.RegisterServerCallback('esx_property:getProperties', function(source, cb)
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_ownedproperty:getOwnedProperties', function(cb)
|
||||
MySQL.Async.fetchAll('SELECT * FROM owned_properties', {}, function(result)
|
||||
MySQL.query('SELECT * FROM owned_properties', {}, function(result)
|
||||
local properties = {}
|
||||
|
||||
for i=1, #result, 1 do
|
||||
@@ -198,7 +198,7 @@ RegisterNetEvent('esx_property:saveLastProperty')
|
||||
AddEventHandler('esx_property:saveLastProperty', function(property)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET last_property = @last_property WHERE identifier = @identifier', {
|
||||
MySQL.update('UPDATE users SET last_property = @last_property WHERE identifier = @identifier', {
|
||||
['@last_property'] = property,
|
||||
['@identifier'] = xPlayer.identifier
|
||||
})
|
||||
@@ -208,7 +208,7 @@ RegisterNetEvent('esx_property:deleteLastProperty')
|
||||
AddEventHandler('esx_property:deleteLastProperty', function()
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET last_property = NULL WHERE identifier = @identifier', {
|
||||
MySQL.update('UPDATE users SET last_property = NULL WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
})
|
||||
end)
|
||||
@@ -315,7 +315,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_property:getOwnedProperties', function(source, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT name, rented FROM owned_properties WHERE owner = @owner', {
|
||||
MySQL.query('SELECT name, rented FROM owned_properties WHERE owner = @owner', {
|
||||
['@owner'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
cb(result)
|
||||
@@ -325,7 +325,7 @@ end)
|
||||
ESX.RegisterServerCallback('esx_property:getLastProperty', function(source, cb)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT last_property FROM users WHERE identifier = @identifier', {
|
||||
MySQL.query('SELECT last_property FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(users)
|
||||
cb(users[1].last_property)
|
||||
@@ -410,7 +410,7 @@ function payRent(d, h, m)
|
||||
local tasks, timeStart = {}, os.clock()
|
||||
print('[esx_property] [^2INFO^7] Paying rent cron job started')
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM owned_properties WHERE rented = 1', {}, function(result)
|
||||
MySQL.query('SELECT * FROM owned_properties WHERE rented = 1', {}, function(result)
|
||||
for k,v in ipairs(result) do
|
||||
table.insert(tasks, function(cb)
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(v.owner)
|
||||
@@ -424,7 +424,7 @@ function payRent(d, h, m)
|
||||
RemoveOwnedProperty(v.name, v.owner, true)
|
||||
end
|
||||
else
|
||||
MySQL.Async.fetchScalar('SELECT accounts FROM users WHERE identifier = @identifier', {
|
||||
MySQL.scalar('SELECT accounts FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = v.owner
|
||||
}, function(accounts)
|
||||
if accounts then
|
||||
@@ -434,7 +434,7 @@ function payRent(d, h, m)
|
||||
if playerAccounts.bank >= v.price then
|
||||
playerAccounts.bank = playerAccounts.bank - v.price
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET accounts = @accounts WHERE identifier = @identifier', {
|
||||
MySQL.update('UPDATE users SET accounts = @accounts WHERE identifier = @identifier', {
|
||||
['@identifier'] = v.owner,
|
||||
['@accounts'] = json.encode(playerAccounts)
|
||||
})
|
||||
|
||||
@@ -9,18 +9,20 @@ function GetSociety(name)
|
||||
end
|
||||
end
|
||||
|
||||
MySQL.ready(function()
|
||||
local result = MySQL.Sync.fetchAll('SELECT * FROM jobs', {})
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
local result = MySQL.query.await('SELECT * FROM jobs')
|
||||
|
||||
for i=1, #result, 1 do
|
||||
Jobs[result[i].name] = result[i]
|
||||
Jobs[result[i].name].grades = {}
|
||||
end
|
||||
for i = 1, #result, 1 do
|
||||
Jobs[result[i].name] = result[i]
|
||||
Jobs[result[i].name].grades = {}
|
||||
end
|
||||
|
||||
local result2 = MySQL.Sync.fetchAll('SELECT * FROM job_grades', {})
|
||||
local result2 = MySQL.query.await('SELECT * FROM job_grades')
|
||||
|
||||
for i=1, #result2, 1 do
|
||||
Jobs[result2[i].job_name].grades[tostring(result2[i].grade)] = result2[i]
|
||||
for i = 1, #result2, 1 do
|
||||
Jobs[result2[i].job_name].grades[tostring(result2[i].grade)] = result2[i]
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -108,11 +110,8 @@ AddEventHandler('esx_society:washMoney', function(society, amount)
|
||||
if amount and amount > 0 and account.money >= amount then
|
||||
xPlayer.removeAccountMoney('black_money', amount)
|
||||
|
||||
MySQL.Async.execute('INSERT INTO society_moneywash (identifier, society, amount) VALUES (@identifier, @society, @amount)', {
|
||||
['@identifier'] = xPlayer.identifier,
|
||||
['@society'] = society,
|
||||
['@amount'] = amount
|
||||
}, function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO society_moneywash (identifier, society, amount) VALUES (?, ?, ?)', {xPlayer.identifier, society, amount},
|
||||
function(rowsChanged)
|
||||
xPlayer.showNotification(_U('you_have', ESX.Math.GroupDigits(amount)))
|
||||
end)
|
||||
else
|
||||
@@ -188,15 +187,14 @@ ESX.RegisterServerCallback('esx_society:getEmployees', function(source, cb, soci
|
||||
})
|
||||
end
|
||||
|
||||
local query = "SELECT identifier, job_grade FROM `users` WHERE `job`=@job ORDER BY job_grade DESC"
|
||||
local query = "SELECT identifier, job_grade FROM `users` WHERE `job`= ? ORDER BY job_grade DESC"
|
||||
|
||||
if Config.EnableESXIdentity then
|
||||
query = "SELECT identifier, job_grade, firstname, lastname FROM `users` WHERE `job`=@job ORDER BY job_grade DESC"
|
||||
query = "SELECT identifier, job_grade, firstname, lastname FROM `users` WHERE `job`= ? ORDER BY job_grade DESC"
|
||||
end
|
||||
|
||||
MySQL.Async.fetchAll(query, {
|
||||
['@job'] = society
|
||||
}, function(result)
|
||||
MySQL.query(query, {society},
|
||||
function(result)
|
||||
for k, row in pairs(result) do
|
||||
local alreadyInTable
|
||||
local identifier = row.identifier
|
||||
@@ -270,11 +268,8 @@ ESX.RegisterServerCallback('esx_society:setJob', function(source, cb, identifier
|
||||
|
||||
cb()
|
||||
else
|
||||
MySQL.Async.execute('UPDATE users SET job = @job, job_grade = @job_grade WHERE identifier = @identifier', {
|
||||
['@job'] = job,
|
||||
['@job_grade'] = grade,
|
||||
['@identifier'] = identifier
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('UPDATE users SET job = ?, job_grade = ? WHERE identifier = ?', {job, grade, identifier},
|
||||
function(rowsChanged)
|
||||
cb()
|
||||
end)
|
||||
end
|
||||
@@ -289,11 +284,8 @@ ESX.RegisterServerCallback('esx_society:setJobSalary', function(source, cb, job,
|
||||
|
||||
if xPlayer.job.name == job and xPlayer.job.grade_name == 'boss' then
|
||||
if salary <= Config.MaxSalary then
|
||||
MySQL.Async.execute('UPDATE job_grades SET salary = @salary WHERE job_name = @job_name AND grade = @grade', {
|
||||
['@salary'] = salary,
|
||||
['@job_name'] = job,
|
||||
['@grade'] = grade
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('UPDATE job_grades SET salary = ? WHERE job_name = ? AND grade = ?', {salary, job, grade},
|
||||
function(rowsChanged)
|
||||
Jobs[job].grades[tostring(grade)].salary = salary
|
||||
|
||||
local xPlayers = ESX.GetExtendedPlayers('job', job)
|
||||
@@ -365,7 +357,7 @@ function isPlayerBoss(playerId, job)
|
||||
end
|
||||
|
||||
function WashMoneyCRON(d, h, m)
|
||||
MySQL.Async.fetchAll('SELECT * FROM society_moneywash', {}, function(result)
|
||||
MySQL.query('SELECT * FROM society_moneywash', function(result)
|
||||
for i=1, #result, 1 do
|
||||
local society = GetSociety(result[i].society)
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(result[i].identifier)
|
||||
@@ -380,10 +372,8 @@ function WashMoneyCRON(d, h, m)
|
||||
xPlayer.showNotification(_U('you_have_laundered', ESX.Math.GroupDigits(result[i].amount)))
|
||||
end
|
||||
|
||||
MySQL.Async.execute('DELETE FROM society_moneywash WHERE id = @id', {
|
||||
['@id'] = result[i].id
|
||||
})
|
||||
end
|
||||
MySQL.update('DELETE FROM society_moneywash')
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -65,12 +65,17 @@ AddEventHandler('esx_status:load', function(status)
|
||||
if Config.Display then TriggerEvent('esx_status:setDisplay', 0.5) end
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
local data = {}
|
||||
while ESX.PlayerLoaded do
|
||||
for i=1, #Status, 1 do
|
||||
for i=1, #Status do
|
||||
Status[i].onTick()
|
||||
table.insert(data, {
|
||||
name = Status[i].name,
|
||||
val = Status[i].val,
|
||||
percent = (Status[i].val / 1000000) * 100
|
||||
})
|
||||
end
|
||||
local data = GetStatusData(true)
|
||||
|
||||
|
||||
if Config.Display then
|
||||
local fullData = data
|
||||
for i=1, #data, 1 do
|
||||
@@ -84,6 +89,7 @@ AddEventHandler('esx_status:load', function(status)
|
||||
end
|
||||
|
||||
TriggerEvent('esx_status:onTick', data)
|
||||
table.wipe(data)
|
||||
Citizen.Wait(Config.TickTime)
|
||||
end
|
||||
end)
|
||||
@@ -154,9 +160,9 @@ AddEventHandler('esx_status:setDisplay', function(val)
|
||||
end)
|
||||
|
||||
-- Pause menu disable hud display
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
if Config.Display then
|
||||
if Config.Display then
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Citizen.Wait(300)
|
||||
|
||||
if IsPauseMenuActive() and not isPaused then
|
||||
@@ -166,9 +172,9 @@ Citizen.CreateThread(function()
|
||||
isPaused = false
|
||||
TriggerEvent('esx_status:setDisplay', 0.5)
|
||||
end
|
||||
else Citizen.Wait(1000) end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Loading screen off event
|
||||
AddEventHandler('esx:loadingScreenOff', function()
|
||||
|
||||
@@ -6,6 +6,8 @@ description 'ESX Status'
|
||||
|
||||
version 'legacy'
|
||||
|
||||
lua54 'yes'
|
||||
|
||||
shared_script '@es_extended/imports.lua'
|
||||
|
||||
server_scripts {
|
||||
@@ -27,3 +29,5 @@ files {
|
||||
'html/css/app.css',
|
||||
'html/scripts/app.js'
|
||||
}
|
||||
|
||||
dependency 'es_extended'
|
||||
|
||||
@@ -1,43 +1,26 @@
|
||||
local function setPlayerStatus(xPlayer, data)
|
||||
data = data and json.decode(data) or {}
|
||||
|
||||
xPlayer.set('status', data)
|
||||
ESX.Players[xPlayer.source] = data
|
||||
TriggerClientEvent('esx_status:load', xPlayer.source, data)
|
||||
end
|
||||
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if (GetCurrentResourceName() ~= resourceName) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
for _, xPlayer in pairs(ESX.Players) do
|
||||
local status = xPlayer.get('status')
|
||||
if status then
|
||||
ESX.Players[xPlayer.source] = status
|
||||
else
|
||||
MySQL.Async.fetchAll('SELECT status FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
local data = {}
|
||||
|
||||
if result[1].status then
|
||||
data = json.decode(result[1].status)
|
||||
end
|
||||
|
||||
xPlayer.set('status', data) -- save to xPlayer for compatibility
|
||||
ESX.Players[xPlayer.source] = data -- save locally for performance
|
||||
end)
|
||||
end
|
||||
TriggerClientEvent('esx_status:load', xPlayer.source, data)
|
||||
MySQL.scalar('SELECT status FROM users WHERE identifier = ?', { xPlayer.identifier }, function(result)
|
||||
setPlayerStatus(xPlayer, result)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('esx:playerLoaded', function(playerId, xPlayer)
|
||||
MySQL.Async.fetchAll('SELECT status FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
local data = {}
|
||||
|
||||
if result[1].status then
|
||||
data = json.decode(result[1].status)
|
||||
end
|
||||
|
||||
xPlayer.set('status', data)
|
||||
ESX.Players[xPlayer.source] = data
|
||||
TriggerClientEvent('esx_status:load', playerId, data)
|
||||
MySQL.scalar('SELECT status FROM users WHERE identifier = ?', { xPlayer.identifier }, function(result)
|
||||
setPlayerStatus(xPlayer, result)
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -45,19 +28,15 @@ AddEventHandler('esx:playerDropped', function(playerId, reason)
|
||||
local xPlayer = ESX.GetPlayerFromId(playerId)
|
||||
local status = ESX.Players[xPlayer.source]
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET status = @status WHERE identifier = @identifier', {
|
||||
['@status'] = json.encode(status),
|
||||
['@identifier'] = xPlayer.identifier
|
||||
}, function(result)
|
||||
ESX.Players[xPlayer.source] = nil
|
||||
end)
|
||||
MySQL.update('UPDATE users SET status = ? WHERE identifier = ?', { json.encode(status), xPlayer.identifier })
|
||||
ESX.Players[xPlayer.source] = nil
|
||||
end)
|
||||
|
||||
AddEventHandler('esx_status:getStatus', function(playerId, statusName, cb)
|
||||
for i=1, #ESX.Players, 1 do
|
||||
local status = ESX.Players[playerId]
|
||||
for i = 1, #status do
|
||||
if status[i].name == statusName then
|
||||
cb(status[i])
|
||||
break
|
||||
return cb(status[i])
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -72,50 +51,17 @@ AddEventHandler('esx_status:update', function(status)
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while(true) do
|
||||
while true do
|
||||
Citizen.Wait(10 * 60 * 1000)
|
||||
SaveData()
|
||||
end
|
||||
end)
|
||||
|
||||
function SaveData()
|
||||
-- Example of a bulk update statement that we are building below
|
||||
--[[
|
||||
UPDATE users
|
||||
SET status = (case when identifier = 'license:123' then '{hunger:45, thirst:23}'
|
||||
when identifier = 'license:456' then '{hunger:1000, thirst:1000}'
|
||||
when identifier = 'license:789' then '{hunger:1000, thirst:1000}'
|
||||
end)
|
||||
WHERE identifier in ('license:123', 'license:456', 'license:789')
|
||||
|
||||
]]
|
||||
local updateStatement = 'UPDATE users SET status = (case %s end) where identifier in (%s)'
|
||||
local whenList = ''
|
||||
local whereList = ''
|
||||
local firstItem = true
|
||||
local playerCount = 0
|
||||
|
||||
local xPlayers = ESX.GetExtendedPlayers()
|
||||
|
||||
for _, xPlayer in pairs(xPlayers) do
|
||||
local status = ESX.Players[xPlayer.source]
|
||||
if status then
|
||||
whenList = whenList .. string.format('when identifier = \'%s\' then \'%s\' ', xPlayer.identifier, json.encode(status))
|
||||
|
||||
if firstItem == false then
|
||||
whereList = whereList .. ', '
|
||||
local parameters = {}
|
||||
for _, xPlayer in pairs(ESX.GetExtendedPlayers()) do
|
||||
local status = ESX.Players[xPlayer.source]
|
||||
if status and next(status) then
|
||||
parameters[#parameters+1] = {json.encode(status), xPlayer.identifier}
|
||||
end
|
||||
whereList = whereList .. string.format('\'%s\'', xPlayer.identifier)
|
||||
|
||||
firstItem = false
|
||||
playerCount = playerCount + 1
|
||||
end
|
||||
if #parameters > 0 then
|
||||
MySQL.prepare('UPDATE users SET status = ? WHERE identifier = ?', parameters)
|
||||
end
|
||||
end
|
||||
|
||||
if playerCount > 0 then
|
||||
local sql = string.format(updateStatement, whenList, whereList)
|
||||
MySQL.Async.execute(sql)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -2,10 +2,11 @@ local HasAlreadyEnteredMarker, IsInShopMenu = false, false
|
||||
local CurrentAction, CurrentActionMsg, LastZone, currentDisplayVehicle, CurrentVehicleData
|
||||
local CurrentActionData, Vehicles, Categories = {}, {}, {}
|
||||
|
||||
function getVehicleLabelFromModel(model)
|
||||
for k,v in ipairs(Vehicles) do
|
||||
if v.model == model then
|
||||
return v.name
|
||||
function getVehicleFromModel(model)
|
||||
for i = 1, #Vehicles do
|
||||
local vehicle = Vehicles[i]
|
||||
if vehicle.model == model then
|
||||
return vehicle
|
||||
end
|
||||
end
|
||||
|
||||
@@ -82,7 +83,7 @@ function ReturnVehicleProvider()
|
||||
|
||||
for k,v in ipairs(vehicles) do
|
||||
local returnPrice = ESX.Math.Round(v.price * 0.75)
|
||||
local vehicleLabel = getVehicleLabelFromModel(v.vehicle)
|
||||
local vehicleLabel = getVehicleFromModel(v.vehicle).label
|
||||
|
||||
table.insert(elements, {
|
||||
label = ('%s [<span style="color:orange;">%s</span>]'):format(vehicleLabel, _U('generic_shopitem', ESX.Math.GroupDigits(returnPrice))),
|
||||
@@ -143,7 +144,7 @@ function OpenShopMenu()
|
||||
end
|
||||
|
||||
for i=1, #Vehicles, 1 do
|
||||
if IsModelInCdimage(GetHashKey(Vehicles[i].model)) then
|
||||
if IsModelInCdimage(joaat(Vehicles[i].model)) then
|
||||
table.insert(vehiclesByCategory[Vehicles[i].category], Vehicles[i])
|
||||
else
|
||||
print(('[esx_vehicleshop] [^3ERROR^7] Vehicle "%s" does not exist'):format(Vehicles[i].model))
|
||||
@@ -289,7 +290,7 @@ function OpenShopMenu()
|
||||
end
|
||||
|
||||
function WaitForVehicleToLoad(modelHash)
|
||||
modelHash = (type(modelHash) == 'number' and modelHash or GetHashKey(modelHash))
|
||||
modelHash = (type(modelHash) == 'number' and modelHash or joaat(modelHash))
|
||||
|
||||
if not HasModelLoaded(modelHash) then
|
||||
RequestModel(modelHash)
|
||||
|
||||
@@ -50,7 +50,6 @@ end
|
||||
|
||||
function GetRandomNumber(length)
|
||||
Citizen.Wait(0)
|
||||
math.randomseed(GetGameTimer())
|
||||
if length > 0 then
|
||||
return GetRandomNumber(length - 1) .. NumberCharset[math.random(1, #NumberCharset)]
|
||||
else
|
||||
@@ -60,7 +59,6 @@ end
|
||||
|
||||
function GetRandomLetter(length)
|
||||
Citizen.Wait(0)
|
||||
math.randomseed(GetGameTimer())
|
||||
if length > 0 then
|
||||
return GetRandomLetter(length - 1) .. Charset[math.random(1, #Charset)]
|
||||
else
|
||||
|
||||
@@ -2,6 +2,8 @@ fx_version 'adamant'
|
||||
|
||||
game 'gta5'
|
||||
|
||||
lua54 'yes'
|
||||
|
||||
description 'ESX Vehicle Shop'
|
||||
|
||||
version 'legacy'
|
||||
|
||||
@@ -14,35 +14,29 @@ Citizen.CreateThread(function()
|
||||
end)
|
||||
|
||||
function RemoveOwnedVehicle(plate)
|
||||
MySQL.Async.execute('DELETE FROM owned_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = plate
|
||||
})
|
||||
MySQL.update('DELETE FROM owned_vehicles WHERE plate = ?', {plate})
|
||||
end
|
||||
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
MySQL.ready(function() SQLVehiclesAndCategories() end)
|
||||
end
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
SQLVehiclesAndCategories()
|
||||
end
|
||||
end)
|
||||
|
||||
function SQLVehiclesAndCategories()
|
||||
MySQL.Async.fetchAll('SELECT * FROM `vehicle_categories`', {}, function(_categories)
|
||||
categories = _categories
|
||||
categories = MySQL.query.await('SELECT * FROM vehicle_categories')
|
||||
vehicles = MySQL.query.await('SELECT * FROM vehicles')
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM `vehicles`', {}, function(_vehicles)
|
||||
vehicles = _vehicles
|
||||
|
||||
GetVehiclesAndCategories(categories, vehicles)
|
||||
end)
|
||||
|
||||
end)
|
||||
GetVehiclesAndCategories(categories, vehicles)
|
||||
end
|
||||
|
||||
function GetVehiclesAndCategories(categories, vehicles)
|
||||
for k,v in ipairs(vehicles) do
|
||||
for k2,v2 in ipairs(categories) do
|
||||
if v2.name == v.category then
|
||||
vehicles[k].categoryLabel = v2.label
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
for j = 1, #categories do
|
||||
local category = categories[j]
|
||||
if category.name == vehicle.catetgory then
|
||||
vehicle.categoryLabel = category.label
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -53,10 +47,11 @@ function GetVehiclesAndCategories(categories, vehicles)
|
||||
TriggerClientEvent('esx_vehicleshop:sendVehicles', -1, vehicles)
|
||||
end
|
||||
|
||||
function getVehicleLabelFromModel(model)
|
||||
for k,v in ipairs(vehicles) do
|
||||
if v.model == model then
|
||||
return v.name
|
||||
function getVehicleFromModel(model)
|
||||
for i = 1, #vehicles do
|
||||
local vehicle = vehicles[i]
|
||||
if vehicle.model == model then
|
||||
return vehicle
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,34 +63,19 @@ AddEventHandler('esx_vehicleshop:setVehicleOwnedPlayerId', function(playerId, ve
|
||||
local xPlayer, xTarget = ESX.GetPlayerFromId(source), ESX.GetPlayerFromId(playerId)
|
||||
|
||||
if xPlayer.job.name == 'cardealer' and xTarget then
|
||||
MySQL.Async.fetchAll('SELECT id FROM cardealer_vehicles WHERE vehicle = @vehicle LIMIT 1', {
|
||||
['@vehicle'] = model
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
local id = result[1].id
|
||||
|
||||
MySQL.Async.execute('DELETE FROM cardealer_vehicles WHERE id = @id', {
|
||||
['@id'] = id
|
||||
}, function(rowsChanged)
|
||||
MySQL.scalar('SELECT id FROM cardealer_vehicles WHERE vehicle = ?', {model},
|
||||
function(id)
|
||||
if id then
|
||||
MySQL.update('DELETE FROM cardealer_vehicles WHERE id = ?', {id},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
MySQL.Async.execute('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (@owner, @plate, @vehicle)', {
|
||||
['@owner'] = xTarget.identifier,
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@vehicle'] = json.encode(vehicleProps)
|
||||
}, function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {xTarget.identifier, vehicleProps.plate, json.encode(vehicleProps)},
|
||||
function(id)
|
||||
xPlayer.showNotification(_U('vehicle_set_owned', vehicleProps.plate, xTarget.getName()))
|
||||
xTarget.showNotification(_U('vehicle_belongs', vehicleProps.plate))
|
||||
end)
|
||||
|
||||
local dateNow = os.date('%Y-%m-%d %H:%M')
|
||||
|
||||
MySQL.Async.execute('INSERT INTO vehicle_sold (client, model, plate, soldby, date) VALUES (@client, @model, @plate, @soldby, @date)', {
|
||||
['@client'] = xTarget.getName(),
|
||||
['@model'] = label,
|
||||
['@plate'] = vehicleProps.plate,
|
||||
['@soldby'] = xPlayer.getName(),
|
||||
['@date'] = dateNow
|
||||
})
|
||||
MySQL.insert('INSERT INTO vehicle_sold (client, model, plate, soldby, date) VALUES (?, ?, ?, ?, ?)', {xTarget.getName(), label, vehicleProps.plate, xPlayer.getName(), os.date('%Y-%m-%d %H:%M')})
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -104,7 +84,7 @@ AddEventHandler('esx_vehicleshop:setVehicleOwnedPlayerId', function(playerId, ve
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:getSoldVehicles', function(source, cb)
|
||||
MySQL.Async.fetchAll('SELECT client, model, plate, soldby, date FROM vehicle_sold', {}, function(result)
|
||||
MySQL.query('SELECT client, model, plate, soldby, date FROM vehicle_sold', function(result)
|
||||
cb(result)
|
||||
end)
|
||||
end)
|
||||
@@ -114,24 +94,14 @@ AddEventHandler('esx_vehicleshop:rentVehicle', function(vehicle, plate, rentPric
|
||||
local xPlayer, xTarget = ESX.GetPlayerFromId(source), ESX.GetPlayerFromId(playerId)
|
||||
|
||||
if xPlayer.job.name == 'cardealer' and xTarget then
|
||||
MySQL.Async.fetchAll('SELECT id, price FROM cardealer_vehicles WHERE vehicle = @vehicle LIMIT 1', {
|
||||
['@vehicle'] = vehicle
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
local price = result[1].price
|
||||
|
||||
MySQL.Async.execute('DELETE FROM cardealer_vehicles WHERE id = @id', {
|
||||
['@id'] = result[1].id
|
||||
}, function(rowsChanged)
|
||||
MySQL.single('SELECT id, price FROM cardealer_vehicles WHERE vehicle = ?', {vehicle},
|
||||
function(result)
|
||||
if result then
|
||||
MySQL.update('DELETE FROM cardealer_vehicles WHERE id = ?', {result.id},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
MySQL.Async.execute('INSERT INTO rented_vehicles (vehicle, plate, player_name, base_price, rent_price, owner) VALUES (@vehicle, @plate, @player_name, @base_price, @rent_price, @owner)', {
|
||||
['@vehicle'] = vehicle,
|
||||
['@plate'] = plate,
|
||||
['@player_name'] = xTarget.getName(),
|
||||
['@base_price'] = price,
|
||||
['@rent_price'] = rentPrice,
|
||||
['@owner'] = xTarget.identifier
|
||||
}, function(rowsChanged2)
|
||||
MySQL.insert('INSERT INTO rented_vehicles (vehicle, plate, player_name, base_price, rent_price, owner) VALUES (?, ?, ?, ?, ?, ?)', {vehicle, plate, xTarget.getName(), result.price, rentPrice, xTarget.identifier},
|
||||
function(id)
|
||||
xPlayer.showNotification(_U('vehicle_set_rented', plate, xTarget.getName()))
|
||||
end)
|
||||
end
|
||||
@@ -194,22 +164,12 @@ end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:buyVehicle', function(source, cb, model, plate)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
local modelPrice
|
||||
|
||||
for k,v in ipairs(vehicles) do
|
||||
if model == v.model then
|
||||
modelPrice = v.price
|
||||
break
|
||||
end
|
||||
end
|
||||
local modelPrice = getVehicleFromModel(model).price
|
||||
|
||||
if modelPrice and xPlayer.getMoney() >= modelPrice then
|
||||
xPlayer.removeMoney(modelPrice)
|
||||
|
||||
MySQL.Async.execute('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (@owner, @plate, @vehicle)', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = plate,
|
||||
['@vehicle'] = json.encode({model = GetHashKey(model), plate = plate})
|
||||
MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {xPlayer.identifier, plate, json.encode({model = joaat(model), plate = plate})
|
||||
}, function(rowsChanged)
|
||||
xPlayer.showNotification(_U('vehicle_belongs', plate))
|
||||
cb(true)
|
||||
@@ -220,7 +180,7 @@ ESX.RegisterServerCallback('esx_vehicleshop:buyVehicle', function(source, cb, mo
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:getCommercialVehicles', function(source, cb)
|
||||
MySQL.Async.fetchAll('SELECT price, vehicle FROM cardealer_vehicles ORDER BY vehicle ASC', {}, function(result)
|
||||
MySQL.query('SELECT price, vehicle FROM cardealer_vehicles ORDER BY vehicle ASC', function(result)
|
||||
cb(result)
|
||||
end)
|
||||
end)
|
||||
@@ -229,24 +189,15 @@ ESX.RegisterServerCallback('esx_vehicleshop:buyCarDealerVehicle', function(sourc
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
if xPlayer.job.name == 'cardealer' then
|
||||
local modelPrice
|
||||
|
||||
for k,v in ipairs(vehicles) do
|
||||
if model == v.model then
|
||||
modelPrice = v.price
|
||||
break
|
||||
end
|
||||
end
|
||||
local modelPrice = getVehicleFromModel(model).price
|
||||
|
||||
if modelPrice then
|
||||
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
|
||||
if account.money >= modelPrice then
|
||||
account.removeMoney(modelPrice)
|
||||
|
||||
MySQL.Async.execute('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (@vehicle, @price)', {
|
||||
['@vehicle'] = model,
|
||||
['@price'] = modelPrice
|
||||
}, function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (?, ?)', {model, modelPrice},
|
||||
function(rowsChanged)
|
||||
cb(true)
|
||||
end)
|
||||
else
|
||||
@@ -262,19 +213,17 @@ AddEventHandler('esx_vehicleshop:returnProvider', function(vehicleModel)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
if xPlayer.job.name == 'cardealer' then
|
||||
MySQL.Async.fetchAll('SELECT id, price FROM cardealer_vehicles WHERE vehicle = @vehicle LIMIT 1', {
|
||||
['@vehicle'] = vehicleModel
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
local id = result[1].id
|
||||
MySQL.single('SELECT id, price FROM cardealer_vehicles WHERE vehicle = ?', {vehicleModel},
|
||||
function(result)
|
||||
if result then
|
||||
local id = result.id
|
||||
|
||||
MySQL.Async.execute('DELETE FROM cardealer_vehicles WHERE id = @id', {
|
||||
['@id'] = id
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('DELETE FROM cardealer_vehicles WHERE id = ?', {id},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 1 then
|
||||
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
|
||||
local price = ESX.Math.Round(result[1].price * 0.75)
|
||||
local vehicleLabel = getVehicleLabelFromModel(vehicleModel)
|
||||
local price = ESX.Math.Round(result.price * 0.75)
|
||||
local vehicleLabel = getVehicleFromModel(vehicleModel).label
|
||||
|
||||
account.addMoney(price)
|
||||
xPlayer.showNotification(_U('vehicle_sold_for', vehicleLabel, ESX.Math.GroupDigits(price)))
|
||||
@@ -289,15 +238,16 @@ AddEventHandler('esx_vehicleshop:returnProvider', function(vehicleModel)
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:getRentedVehicles', function(source, cb)
|
||||
MySQL.Async.fetchAll('SELECT * FROM rented_vehicles ORDER BY player_name ASC', {}, function(result)
|
||||
MySQL.query('SELECT * FROM rented_vehicles ORDER BY player_name ASC', function(result)
|
||||
local vehicles = {}
|
||||
|
||||
for i=1, #result, 1 do
|
||||
table.insert(vehicles, {
|
||||
name = result[i].vehicle,
|
||||
plate = result[i].plate,
|
||||
playerName = result[i].player_name
|
||||
})
|
||||
for i = 1, #result do
|
||||
local vehicle = tab[i]
|
||||
vehicles[#vehicles + 1] = {
|
||||
name = vehicle.vehicle,
|
||||
plate = vehicle.plate,
|
||||
playerName = vehicle.player_name
|
||||
}
|
||||
end
|
||||
|
||||
cb(vehicles)
|
||||
@@ -305,20 +255,15 @@ ESX.RegisterServerCallback('esx_vehicleshop:getRentedVehicles', function(source,
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:giveBackVehicle', function(source, cb, plate)
|
||||
MySQL.Async.fetchAll('SELECT base_price, vehicle FROM rented_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = plate
|
||||
}, function(result)
|
||||
if result[1] then
|
||||
local vehicle = result[1].vehicle
|
||||
local basePrice = result[1].base_price
|
||||
MySQL.single('SELECT base_price, vehicle FROM rented_vehicles WHERE plate = ?', {plate},
|
||||
function(result)
|
||||
if result then
|
||||
local vehicle = result.vehicle
|
||||
local basePrice = result.base_price
|
||||
|
||||
MySQL.Async.execute('DELETE FROM rented_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = plate
|
||||
}, function(rowsChanged)
|
||||
MySQL.Async.execute('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (@vehicle, @price)', {
|
||||
['@vehicle'] = vehicle,
|
||||
['@price'] = basePrice
|
||||
})
|
||||
MySQL.update('DELETE FROM rented_vehicles WHERE plate = ?', {plate},
|
||||
function(rowsChanged)
|
||||
MySQL.insert('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (?, ?)', {result.vehicle, result.base_price})
|
||||
|
||||
RemoveOwnedVehicle(plate)
|
||||
cb(true)
|
||||
@@ -335,7 +280,7 @@ ESX.RegisterServerCallback('esx_vehicleshop:resellVehicle', function(source, cb,
|
||||
if xPlayer.job.name == 'cardealer' or not Config.EnablePlayerManagement then
|
||||
-- calculate the resell price
|
||||
for i=1, #vehicles, 1 do
|
||||
if GetHashKey(vehicles[i].model) == model then
|
||||
if joaat(vehicles[i].model) == model then
|
||||
resellPrice = ESX.Math.Round(vehicles[i].price / 100 * Config.ResellPercentage)
|
||||
break
|
||||
end
|
||||
@@ -345,18 +290,15 @@ ESX.RegisterServerCallback('esx_vehicleshop:resellVehicle', function(source, cb,
|
||||
print(('[esx_vehicleshop] [^3WARNING^7] %s attempted to sell an unknown vehicle!'):format(xPlayer.identifier))
|
||||
cb(false)
|
||||
else
|
||||
MySQL.Async.fetchAll('SELECT * FROM rented_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = plate
|
||||
}, function(result)
|
||||
if result[1] then -- is it a rented vehicle?
|
||||
MySQL.single('SELECT * FROM rented_vehicles WHERE plate = ?', {plate},
|
||||
function(result)
|
||||
if result then -- is it a rented vehicle?
|
||||
cb(false) -- it is, don't let the player sell it since he doesn't own it
|
||||
else
|
||||
MySQL.Async.fetchAll('SELECT * FROM owned_vehicles WHERE owner = @owner AND @plate = plate', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@plate'] = plate
|
||||
}, function(result)
|
||||
if result[1] then -- does the owner match?
|
||||
local vehicle = json.decode(result[1].vehicle)
|
||||
MySQL.single('SELECT * FROM owned_vehicles WHERE owner = ? AND plate = ?', {xPlayer.identifier, plate},
|
||||
function(result)
|
||||
if result then -- does the owner match?
|
||||
local vehicle = json.decode(result.vehicle)
|
||||
|
||||
if vehicle.model == model then
|
||||
if vehicle.plate == plate then
|
||||
@@ -393,21 +335,17 @@ ESX.RegisterServerCallback('esx_vehicleshop:getPlayerInventory', function(source
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:isPlateTaken', function(source, cb, plate)
|
||||
MySQL.Async.fetchAll('SELECT 1 FROM owned_vehicles WHERE plate = @plate', {
|
||||
['@plate'] = plate
|
||||
}, function(result)
|
||||
cb(result[1] ~= nil)
|
||||
MySQL.scalar('SELECT plate FROM owned_vehicles WHERE plate = ?', {plate},
|
||||
function(result)
|
||||
cb(result ~= nil)
|
||||
end)
|
||||
end)
|
||||
|
||||
ESX.RegisterServerCallback('esx_vehicleshop:retrieveJobVehicles', function(source, cb, type)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM owned_vehicles WHERE owner = @owner AND type = @type AND job = @job', {
|
||||
['@owner'] = xPlayer.identifier,
|
||||
['@type'] = type,
|
||||
['@job'] = xPlayer.job.name
|
||||
}, function(result)
|
||||
MySQL.query('SELECT * FROM owned_vehicles WHERE owner = ? AND type = ? AND job = ?', {xPlayer.identifier, type, xPlayer.job.name},
|
||||
function(result)
|
||||
cb(result)
|
||||
end)
|
||||
end)
|
||||
@@ -416,76 +354,101 @@ RegisterNetEvent('esx_vehicleshop:setJobVehicleState')
|
||||
AddEventHandler('esx_vehicleshop:setJobVehicleState', function(plate, state)
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
|
||||
MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = @stored WHERE plate = @plate AND job = @job', {
|
||||
['@stored'] = state,
|
||||
['@plate'] = plate,
|
||||
['@job'] = xPlayer.job.name
|
||||
}, function(rowsChanged)
|
||||
MySQL.update('UPDATE owned_vehicles SET `stored` = ? WHERE plate = ? AND job = ?', {state, plate, xPlayer.job.name},
|
||||
function(rowsChanged)
|
||||
if rowsChanged == 0 then
|
||||
print(('[esx_vehicleshop] [^3WARNING^7] %s exploited the garage!'):format(xPlayer.identifier))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
function UnrentVehicleAsync(identifier, plate)
|
||||
MySQL.Async.execute('DELETE FROM rented_vehicles WHERE identifier = @identifier AND plate = @plate', {
|
||||
['@identifier'] = identifier,
|
||||
['@plate'] = plate
|
||||
})
|
||||
end
|
||||
|
||||
function PayRent(d, h, m)
|
||||
local tasks, timeStart = {}, os.clock()
|
||||
function PayRent()
|
||||
local timeStart = os.clock()
|
||||
print('[esx_vehicleshop] [^2INFO^7] Paying rent cron job started')
|
||||
|
||||
MySQL.Async.fetchAll('SELECT owner, rent_price, plate FROM rented_vehicles', {}, function(result)
|
||||
for k,v in ipairs(result) do
|
||||
table.insert(tasks, function(cb)
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(v.owner)
|
||||
MySQL.query('SELECT rented_vehicles.owner, rented_vehicles.rent_price, rented_vehicles.plate, users.accounts FROM rented_vehicles LEFT JOIN users ON rented_vehicles.owner = users.identifier', {},
|
||||
function(rentals)
|
||||
local owners = {}
|
||||
for i = 1, #rentals do
|
||||
local rental = rentals[i]
|
||||
if not owners[rental.owner] then
|
||||
owners[rental.owner] = {rental}
|
||||
else
|
||||
owners[rental.owner][#owners[rental.owner] + 1] = rental
|
||||
end
|
||||
end
|
||||
|
||||
if xPlayer then
|
||||
if xPlayer.getAccount('bank').money >= v.rent_price then
|
||||
xPlayer.removeAccountMoney('bank', v.rent_price)
|
||||
xPlayer.showNotification(_U('paid_rental', ESX.Math.GroupDigits(v.rent_price), v.plate))
|
||||
else
|
||||
xPlayer.showNotification(_U('paid_rental_evicted', ESX.Math.GroupDigits(v.rent_price), v.plate))
|
||||
UnrentVehicleAsync(v.owner, v.plate)
|
||||
end
|
||||
local total = 0
|
||||
local unrentals = {}
|
||||
local users = {}
|
||||
for k, v in pairs(owners) do
|
||||
local sum = 0
|
||||
for i = 1, #v do
|
||||
sum += v[i].rent_price
|
||||
end
|
||||
local xPlayer = ESX.GetPlayerFromIdentifier(k)
|
||||
|
||||
if xPlayer then
|
||||
local bank = xPlayer.getAccount('bank').money
|
||||
|
||||
if bank >= sum and #v > 1 then
|
||||
total += sum
|
||||
xPlayer.removeAccountMoney('bank', sum)
|
||||
xPlayer.showNotification(('You have paid ~g~$%s~s~ for all of your rentals'):format(ESX.Math.GroupDigits(sum)))
|
||||
else
|
||||
MySQL.Async.fetchScalar('SELECT accounts FROM users WHERE identifier = @identifier', {
|
||||
['@identifier'] = v.owner
|
||||
}, function(accounts)
|
||||
if accounts then
|
||||
local playerAccounts = json.decode(accounts)
|
||||
|
||||
if playerAccounts and playerAccounts.bank then
|
||||
if playerAccounts.bank >= v.price then
|
||||
playerAccounts.bank = playerAccounts.bank - v.price
|
||||
|
||||
MySQL.Async.execute('UPDATE users SET accounts = @accounts WHERE identifier = @identifier', {
|
||||
['@identifier'] = v.owner,
|
||||
['@accounts'] = json.encode(playerAccounts)
|
||||
})
|
||||
else
|
||||
UnrentVehicleAsync(v.owner, v.plate)
|
||||
end
|
||||
end
|
||||
for i = 1, #v do
|
||||
local rental = v[i]
|
||||
if xPlayer.getAccount('bank').money >= rental.rent_price then
|
||||
total += rental.rent_price
|
||||
xPlayer.removeAccountMoney('bank', rental.rent_price)
|
||||
xPlayer.showNotification(_U('paid_rental', ESX.Math.GroupDigits(rental.rent_price), rental.plate))
|
||||
else
|
||||
xPlayer.showNotification(_U('paid_rental_evicted', ESX.Math.GroupDigits(rental.rent_price), rental.plate))
|
||||
unrentals[#unrentals + 1] = {rental.owner, rental.plate}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
else
|
||||
local accounts = json.decode(v[1].accounts)
|
||||
if accounts.bank < sum then
|
||||
sum = 0
|
||||
local limit = false
|
||||
for i = 1, #v do
|
||||
local rental = v[i]
|
||||
if not limit then
|
||||
sum += rental.rent_price
|
||||
if sum > accounts.bank then
|
||||
sum -= rental.rent_price
|
||||
limit = true
|
||||
end
|
||||
else
|
||||
unrentals[#unrentals + 1] = {rental.owner, rental.plate}
|
||||
end
|
||||
end
|
||||
end
|
||||
if sum > 0 then
|
||||
total += sum
|
||||
accounts.bank -= sum
|
||||
users[#users + 1] = {json.encode(accounts), k}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
|
||||
account.addMoney(result[i].rent_price)
|
||||
end)
|
||||
|
||||
cb()
|
||||
if total > 0 then
|
||||
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
|
||||
account.addMoney(total)
|
||||
end)
|
||||
end
|
||||
|
||||
Async.parallelLimit(tasks, 5, function(results) end)
|
||||
if next(users) then
|
||||
MySQL.prepare.await('UPDATE users SET accounts = ? WHERE identifier = ?', users)
|
||||
end
|
||||
|
||||
local elapsedTime = os.clock() - timeStart
|
||||
print(('[esx_vehicleshop] [^2INFO^7] Paying rent cron job took %s seconds'):format(elapsedTime))
|
||||
if next(unrentals) then
|
||||
MySQL.prepare.await('DELETE FROM rented_vehicles WHERE owner = ? AND plate = ?', unrentals)
|
||||
end
|
||||
|
||||
print(('[esx_vehicleshop] [^2INFO^7] Paying rent cron job took %s seconds'):format(os.clock() - timeStart))
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ local shopItems = {}
|
||||
|
||||
MySQL.ready(function()
|
||||
|
||||
MySQL.Async.fetchAll('SELECT * FROM weashops', {}, function(result)
|
||||
MySQL.query('SELECT * FROM weashops', {}, function(result)
|
||||
for i=1, #result, 1 do
|
||||
if shopItems[result[i].zone] == nil then
|
||||
shopItems[result[i].zone] = {}
|
||||
@@ -77,7 +77,7 @@ ESX.RegisterServerCallback('esx_weaponshop:buyWeapon', function(source, cb, weap
|
||||
end)
|
||||
|
||||
function GetPrice(weaponName, zone)
|
||||
local price = MySQL.Sync.fetchScalar('SELECT price FROM weashops WHERE zone = @zone AND item = @item', {
|
||||
local price = MySQL.scalar.await('SELECT price FROM weashops WHERE zone = @zone AND item = @item', {
|
||||
['@zone'] = zone,
|
||||
['@item'] = weaponName
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ ESX.RegisterCommand('wladd', 'admin', function(xPlayer, args, showError)
|
||||
if WhiteList[args.license] then
|
||||
showError('The player is already whitelisted on this server!')
|
||||
else
|
||||
MySQL.Async.execute('INSERT INTO whitelist (identifier) VALUES (@identifier)', {
|
||||
MySQL.update('INSERT INTO whitelist (identifier) VALUES (@identifier)', {
|
||||
['@identifier'] = args.license
|
||||
}, function(rowsChanged)
|
||||
WhiteList[args.license] = true
|
||||
|
||||
@@ -3,7 +3,7 @@ WhiteList = {}
|
||||
function loadWhiteList(cb)
|
||||
Whitelist = {}
|
||||
|
||||
MySQL.Async.fetchAll('SELECT identifier FROM whitelist', {}, function(result)
|
||||
MySQL.query('SELECT identifier FROM whitelist', {}, function(result)
|
||||
for k,v in ipairs(result) do
|
||||
WhiteList[v.identifier] = true
|
||||
end
|
||||
|
||||
+1041
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user