From 72450dfcd67dd4db09829cb1ce78bb0542dc314d Mon Sep 17 00:00:00 2001 From: Linden <65407488+thelindat@users.noreply.github.com> Date: Sat, 12 Feb 2022 05:58:45 +1100 Subject: [PATCH] refactor: swap to oxmysql ESX officially uses oxmysql with new syntax, so follow suite. --- client/main.lua | 8 ++-- config.lua | 8 ++-- esx_multicharacter.sql | 10 ++--- fxmanifest.lua | 2 +- readme.md | 46 ++++++---------------- server/commands.lua | 14 +++---- server/main.lua | 87 +++++++++++++++++++++--------------------- 7 files changed, 74 insertions(+), 101 deletions(-) diff --git a/client/main.lua b/client/main.lua index 9aa1a919..cb10ca8f 100644 --- a/client/main.lua +++ b/client/main.lua @@ -51,7 +51,7 @@ if ESX.GetConfig().Multichar then 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 + for i = 1, #keys do EnableControlAction(0, keys[i], true) end SetEntityVisible(PlayerPedId(), 0, 0) @@ -64,7 +64,7 @@ if ESX.GetConfig().Multichar then HideHudAndRadarThisFrame() Citizen.Wait(0) local vehicles = GetGamePool('CVehicle') - for i=1, #vehicles do + for i = 1, #vehicles do SetEntityLocallyInvisible(vehicles[i]) end end @@ -80,7 +80,7 @@ if ESX.GetConfig().Multichar then local playerPool = {} while hidePlayers do local players = GetActivePlayers() - for i=1, #players do + for i = 1, #players do local player = players[i] if player ~= PlayerId() and not playerPool[player] then playerPool[player] = true @@ -236,7 +236,7 @@ if ESX.GetConfig().Multichar then else ESX.UI.Menu.CloseAll() local GetSlot = function() - for i=1, slots do + for i = 1, slots do if not Characters[i] then return i end diff --git a/config.lua b/config.lua index 503c9cfb..fe18b776 100644 --- a/config.lua +++ b/config.lua @@ -1,6 +1,9 @@ Config = {} Config.Locale = 'en' +-- Allows players to delete their characters +Config.CanDelete = true + if IsDuplicityVersion() then -- This is the default number of slots for EVERY player -- If you want to manage extra slots for specific players you can do it by using '/setslots' and '/remslots' commands @@ -8,7 +11,6 @@ if IsDuplicityVersion() then -------------------- -- Text to prepend to each character (char#:identifier) - keep it short - -- if you change the length, you will need to modify es_extended (refer to the readme) Config.Prefix = 'char' -------------------- @@ -21,10 +23,6 @@ else Config.Spawn = vector4(-113.7, 565.3, 195.2, 0) -------------------- - -- Allows players to delete their characters - Config.CanDelete = true - -------------------- - -- 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 = true diff --git a/esx_multicharacter.sql b/esx_multicharacter.sql index b3a76af7..f19f7610 100644 --- a/esx_multicharacter.sql +++ b/esx_multicharacter.sql @@ -1,13 +1,9 @@ CREATE TABLE `multicharacter_slots` ( - `identifier` VARCHAR(60) NOT NULL COLLATE 'utf8mb4_unicode_ci', + `identifier` VARCHAR(60) NOT NULL, `slots` INT(11) NOT NULL, PRIMARY KEY (`identifier`) USING BTREE, INDEX `slots` (`slots`) USING BTREE -) -COLLATE='utf8mb4_unicode_ci' -ENGINE=InnoDB -; +) ENGINE=InnoDB; ALTER TABLE `users` ADD COLUMN - `disabled` TINYINT(1) NULL DEFAULT '0' -; + `disabled` TINYINT(1) NULL DEFAULT '0'; diff --git a/fxmanifest.lua b/fxmanifest.lua index f93b5e33..0b928219 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -19,7 +19,7 @@ shared_scripts { server_scripts { '@es_extended/imports.lua', - '@mysql-async/lib/MySQL.lua', + '@oxmysql/lib/MySQL.lua', 'server/*.lua', } diff --git a/readme.md b/readme.md index e4520790..fa8c8748 100644 --- a/readme.md +++ b/readme.md @@ -1,24 +1,6 @@ ### Requirements (ensure you are using the latest) - [ESX Legacy](https://github.com/esx-framework/esx-legacy) -- [Spawnmanager](https://github.com/citizenfx/cfx-server-data/tree/master/resources/%5Bmanagers%5D/spawnmanager) - -- [OxMySQL](https://github.com/overextended/oxmysql/) or [MySQL Async 3.3.2](https://github.com/brouznouf/fivem-mysql-async/releases/tag/3.3.2) - -### Installation -- Modify your ESX config with `Config.Multichar = true` -- Set your database name for `Config.Database` in server/main.lua -- Run `esx_multicharacter.sql` into your database -- All owner and identifier columns should be set to `VARCHAR(60)` to ensure correct data entry - - The resource will attempt to set columns automatically - -### Changing character prefix -- Each character is created a modified identifier (char#:identifier) -- Due to an oversight, ESX Legacy only supports a prefix with a length of 4 -- If you change the length, refer to https://github.com/esx-framework/esx-legacy/blob/main/%5Besx%5D/es_extended/server/classes/player.lua#L17 -- Modify this line to the following -```lua -if Config.Multichar then self.license = 'license:'..identifier:sub(identifier:find(':'), identifier:len()) else self.license = 'license:'..identifier end -``` +- [OxMySQL](https://github.com/overextended/oxmysql/releases) ### Conflicts * The following resources should not be used with ESX Legacy and can result in errors @@ -28,13 +10,15 @@ if Config.Multichar then self.license = 'license:'..identifier:sub(identifier:fi - 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 +### Installation +- Modify your ESX config with `Config.Multichar = true` +- Set your database name for `Config.Database` in server/main.lua +- Run `esx_multicharacter.sql` into your database +- All owner and identifier columns should be set to `VARCHAR(60)` to ensure correct data entry + - The resource will attempt to set columns automatically -#### The menu interface is esx_menu_default - you can use any version if you want a different appearance + +#### The menu is esx_menu_default - you can use any version if you want a different appearance ![image](https://user-images.githubusercontent.com/65407488/126976325-17cc3241-bb9e-451f-a6ed-610a8ef52fa5.png) ### Relogging @@ -43,22 +27,16 @@ if Config.Multichar then self.license = 'license:'..identifier:sub(identifier:fi - 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) +RegisterNetEvent('esx:playerLoaded', function(xPlayer) ESX.PlayerData = xPlayer ESX.PlayerLoaded = true end) -RegisterNetEvent('esx:onPlayerLogout') -AddEventHandler('esx:onPlayerLogout', function() +RegisterNetEvent('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 @@ -76,7 +54,7 @@ end) ## Notice -Copyright© 2021 Linden and KASH +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 diff --git a/server/commands.lua b/server/commands.lua index 6c0ff047..3cfa98d5 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -1,16 +1,16 @@ ESX.RegisterCommand('setslots', 'admin', function(xPlayer, args, showError) - local slots = MySQL.Sync.fetchScalar('SELECT `slots` FROM `multicharacter_slots` WHERE identifier = ?', { + local slots = MySQL.scalar('SELECT `slots` FROM `multicharacter_slots` WHERE identifier = ?', { args.identifier }) if slots == nil then - MySQL.Async.execute('INSERT INTO `multicharacter_slots` (`identifier`, `slots`) VALUES (?, ?)', { + MySQL.update('INSERT INTO `multicharacter_slots` (`identifier`, `slots`) VALUES (?, ?)', { args.identifier, args.slots }) xPlayer.triggerEvent('esx:showNotification', _U('slotsadd', args.slots, args.identifier)) else - MySQL.Async.execute('UPDATE `multicharacter_slots` SET `slots` = ? WHERE `identifier` = ?', { + MySQL.update('UPDATE `multicharacter_slots` SET `slots` = ? WHERE `identifier` = ?', { args.slots, args.identifier }) @@ -22,12 +22,12 @@ end, true, {help = _U('command_setslots'), validate = true, arguments = { }}) ESX.RegisterCommand('remslots', 'admin', function(xPlayer, args, showError) - local slots = MySQL.Sync.fetchScalar('SELECT `slots` FROM `multicharacter_slots` WHERE identifier = ?', { + local slots = MySQL.scalar('SELECT `slots` FROM `multicharacter_slots` WHERE identifier = ?', { args.identifier }) if slots ~= nil then - MySQL.Async.execute('DELETE FROM `multicharacter_slots` WHERE `identifier` = ?', { + MySQL.update('DELETE FROM `multicharacter_slots` WHERE `identifier` = ?', { args.identifier }) xPlayer.triggerEvent('esx:showNotification', _U('slotsrem', args.identifier)) @@ -40,7 +40,7 @@ ESX.RegisterCommand('enablechar', 'admin', function(xPlayer, args, showError) local selectedCharacter = 'char'..args.charslot..':'..args.identifier; - MySQL.Async.execute('UPDATE `users` SET `disabled` = 0 WHERE identifier = ?', { + MySQL.update('UPDATE `users` SET `disabled` = 0 WHERE identifier = ?', { selectedCharacter }, function(result) if result > 0 then @@ -59,7 +59,7 @@ ESX.RegisterCommand('disablechar', 'admin', function(xPlayer, args, showError) local selectedCharacter = 'char'..args.charslot..':'..args.identifier; - MySQL.Async.execute('UPDATE `users` SET `disabled` = 1 WHERE identifier = ?', { + MySQL.update('UPDATE `users` SET `disabled` = 1 WHERE identifier = ?', { selectedCharacter }, function(result) if result > 0 then diff --git a/server/main.lua b/server/main.lua index cb4ad80d..a0c13e15 100644 --- a/server/main.lua +++ b/server/main.lua @@ -10,7 +10,7 @@ elseif ESX.GetConfig().Multichar == true then DATABASE = connectionString:sub(connectionString:find('/')+1, -1):gsub('[%?]+[%w%p]*$', '') else connectionString = {string.strsplit(';', connectionString)} - for i=1, #connectionString do + for i = 1, #connectionString do local v = connectionString[i] if v:match('database') then DATABASE = v:sub(10, #v) @@ -48,48 +48,51 @@ elseif ESX.GetConfig().Multichar == true then local identifier = GetIdentifier(source) ESX.Players[identifier] = true - local slots = MySQL.Sync.fetchScalar("SELECT slots FROM multicharacter_slots WHERE identifier = ?", { - identifier - }) or SLOTS + local slots = MySQL.scalar('SELECT slots FROM multicharacter_slots WHERE identifier = ?', { identifier }) or SLOTS identifier = PREFIX..'%:'..identifier - MySQL.Async.fetchAll(FETCH, {identifier, slots}, function(result) - local characters - if result then - local characterCount = #result - characters = table.create(0, characterCount) - for i=1, characterCount, 1 do - local i = result[i] - local job, grade = i.job or 'unemployed', tostring(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(i.accounts) - local id = tonumber(string.sub(i.identifier, #PREFIX+1, string.find(i.identifier, ':')-1)) - characters[id] = { - id = id, - bank = accounts.bank, - money = accounts.money, - job = job, - job_grade = grade, - firstname = i.firstname, - lastname = i.lastname, - dateofbirth = i.dateofbirth, - skin = json.decode(i.skin), - disabled = i.disabled, - sex = i.sex == 'm' and _('male') or _('female') - } + local result = MySQL.query.await(FETCH, {identifier, slots}) + local characters + + if result then + local characterCount = #result + characters = table.create(0, characterCount) + + for i = 1, characterCount, 1 do + local v = result[i] + local job, grade = v.job or 'unemployed', tostring(v.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(v.accounts) + local id = tonumber(string.sub(v.identifier, #PREFIX+1, string.find(v.identifier, ':')-1)) + + characters[id] = { + id = id, + bank = accounts.bank, + money = accounts.money, + job = job, + job_grade = grade, + firstname = v.firstname, + lastname = v.lastname, + dateofbirth = v.dateofbirth, + skin = v.skin and json.decode(v.skin) or {}, + disabled = v.disabled, + sex = v.sex == 'm' and _('male') or _('female') + } end - TriggerClientEvent('esx_multicharacter:SetupUI', source, characters, slots) - end) + end + + TriggerClientEvent('esx_multicharacter:SetupUI', source, characters, slots) end AddEventHandler('playerConnecting', function(playerName, setKickReason, deferrals) deferrals.defer() local identifier = GetIdentifier(source) - Citizen.Wait(100) + if identifier then if ESX.Players[identifier] then deferrals.done(('A player is already connected to the server with this identifier.\nYour identifier: %s:%s'):format(PRIMARY_IDENTIFIER, identifier)) @@ -112,7 +115,7 @@ elseif ESX.GetConfig().Multichar == true then queries[i] = {query = query, values = {v.table, v.column, identifier}} end - MySQL.Async.transaction(queries, function(result) + MySQL.transaction(queries, function(result) if result then print(('[^2INFO^7] Player [%s] %s has deleted a character (%s)'):format(GetPlayerName(source), source, identifier)) Citizen.Wait(50) @@ -163,14 +166,14 @@ elseif ESX.GetConfig().Multichar == true then end) end) - RegisterServerEvent("esx_multicharacter:SetupCharacters") + RegisterNetEvent('esx_multicharacter:SetupCharacters', function() AddEventHandler('esx_multicharacter:SetupCharacters', function() SetupCharacters(source) end) local awaitingRegistration = {} - RegisterServerEvent("esx_multicharacter:CharacterChosen") - AddEventHandler('esx_multicharacter:CharacterChosen', function(charid, isNew) + + RegisterNetEvent('esx_multicharacter:CharacterChosen', function(charid, isNew) if type(charid) == 'number' and string.len(charid) <= 2 and type(isNew) == 'boolean' then if isNew then awaitingRegistration[source] = charid @@ -192,15 +195,13 @@ elseif ESX.GetConfig().Multichar == true then ESX.Players[GetIdentifier(source)] = nil end) - RegisterServerEvent("esx_multicharacter:DeleteCharacter") - AddEventHandler('esx_multicharacter:DeleteCharacter', function(charid) - if type(charid) == "number" and string.len(charid) <= 2 then + RegisterNetEvent('esx_multicharacter:DeleteCharacter', function(charid) + if Config.CanDelete and type(charid) == 'number' and string.len(charid) <= 2 then DeleteCharacter(source, charid) end end) - RegisterServerEvent("esx_multicharacter:relog") - AddEventHandler('esx_multicharacter:relog', function() + RegisterNetEvent('esx_multicharacter:relog', function() TriggerEvent('esx:playerLogout', source) end)