diff --git a/README.md b/README.md index c2edec3f..2d466b10 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,7 @@ start esx_identity ### Commands ``` -/register -/charlist -/charselect +/char /chardel ``` diff --git a/client/main.lua b/client/main.lua index 45d474e8..238a485e 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,7 +1,5 @@ -local guiEnabled, hasIdentity, isDead = false, false, false -local myIdentity, myIdentifiers = {}, {} - ESX = nil +local loadingScreenFinished = false Citizen.CreateThread(function() while ESX == nil do @@ -10,158 +8,88 @@ Citizen.CreateThread(function() end end) -AddEventHandler('esx:onPlayerDeath', function(data) - isDead = true -end) - -AddEventHandler('playerSpawned', function(spawn) - isDead = false -end) - -function EnableGui(state) - SetNuiFocus(state, state) - guiEnabled = state - - SendNUIMessage({ - type = "enableui", - enable = state - }) -end - -RegisterNetEvent('esx_identity:showRegisterIdentity') -AddEventHandler('esx_identity:showRegisterIdentity', function() - if not isDead then - EnableGui(true) +RegisterNetEvent('esx_identity:alreadyRegistered') +AddEventHandler('esx_identity:alreadyRegistered', function() + while not loadingScreenFinished do + Citizen.Wait(100) end + + TriggerEvent('esx_skin:playerRegistered') end) -RegisterNetEvent('esx_identity:identityCheck') -AddEventHandler('esx_identity:identityCheck', function(identityCheck) - hasIdentity = identityCheck +AddEventHandler('esx:loadingScreenOff', function() + loadingScreenFinished = true end) -RegisterNetEvent('esx_identity:saveID') -AddEventHandler('esx_identity:saveID', function(data) - myIdentifiers = data -end) +if not Config.UseDeferrals then + local guiEnabled, isDead = false, false -RegisterNUICallback('escape', function(data, cb) - if hasIdentity then - EnableGui(false) - else - ESX.ShowNotification(_U('create_a_character')) + AddEventHandler('esx:onPlayerDeath', function(data) + isDead = true + end) + + AddEventHandler('esx:onPlayerSpawn', function(spawn) + isDead = false + end) + + function EnableGui(state) + SetNuiFocus(state, state) + guiEnabled = state + + SendNUIMessage({ + type = "enableui", + enable = state + }) end -end) -RegisterNUICallback('register', function(data, cb) - local reason = "" - myIdentity = data - for theData, value in pairs(myIdentity) do - if theData == "firstname" or theData == "lastname" then - reason = verifyName(value) - - if reason ~= "" then - break - end - elseif theData == "dateofbirth" then - if value == "invalid" then - reason = "Invalid date of birth!" - break - end - elseif theData == "height" then - local height = tonumber(value) - if height then - if height > 200 or height < 140 then - reason = "Unacceptable player height!" - break - end + RegisterNetEvent('esx_identity:showRegisterIdentity') + AddEventHandler('esx_identity:showRegisterIdentity', function() + TriggerEvent('esx_skin:resetFirstSpawn') + + if not isDead then + EnableGui(true) + end + end) + + RegisterNUICallback('register', function(data, cb) + ESX.TriggerServerCallback('esx_identity:registerIdentity', function(callback) + if callback then + ESX.ShowNotification(_U('thank_you_for_registering')) + EnableGui(false) + TriggerEvent('esx_skin:playerRegistered') else - reason = "Unacceptable player height!" - break + ESX.ShowNotification(_U('registration_error')) + end + end, data) + end) + + Citizen.CreateThread(function() + while true do + Citizen.Wait(0) + + if guiEnabled then + DisableControlAction(0, 1, true) -- LookLeftRight + DisableControlAction(0, 2, true) -- LookUpDown + DisableControlAction(0, 106, true) -- VehicleMouseControlOverride + DisableControlAction(0, 142, true) -- MeleeAttackAlternate + DisableControlAction(0, 30, true) -- MoveLeftRight + DisableControlAction(0, 31, true) -- MoveUpDown + DisableControlAction(0, 21, true) -- disable sprint + DisableControlAction(0, 24, true) -- disable attack + DisableControlAction(0, 25, true) -- disable aim + DisableControlAction(0, 47, true) -- disable weapon + DisableControlAction(0, 58, true) -- disable weapon + DisableControlAction(0, 263, true) -- disable melee + DisableControlAction(0, 264, true) -- disable melee + DisableControlAction(0, 257, true) -- disable melee + DisableControlAction(0, 140, true) -- disable melee + DisableControlAction(0, 141, true) -- disable melee + DisableControlAction(0, 143, true) -- disable melee + DisableControlAction(0, 75, true) -- disable exit vehicle + DisableControlAction(27, 75, true) -- disable exit vehicle + else + Citizen.Wait(500) end end - end - - if reason == "" then - TriggerServerEvent('esx_identity:setIdentity', data, myIdentifiers) - EnableGui(false) - Citizen.Wait(500) - TriggerEvent('esx_skin:openSaveableMenu', myIdentifiers.id) - else - ESX.ShowNotification(reason) - end -end) - -Citizen.CreateThread(function() - while true do - Citizen.Wait(0) - - if guiEnabled then - DisableControlAction(0, 1, true) -- LookLeftRight - DisableControlAction(0, 2, true) -- LookUpDown - DisableControlAction(0, 106, true) -- VehicleMouseControlOverride - DisableControlAction(0, 142, true) -- MeleeAttackAlternate - DisableControlAction(0, 30, true) -- MoveLeftRight - DisableControlAction(0, 31, true) -- MoveUpDown - DisableControlAction(0, 21, true) -- disable sprint - DisableControlAction(0, 24, true) -- disable attack - DisableControlAction(0, 25, true) -- disable aim - DisableControlAction(0, 47, true) -- disable weapon - DisableControlAction(0, 58, true) -- disable weapon - DisableControlAction(0, 263, true) -- disable melee - DisableControlAction(0, 264, true) -- disable melee - DisableControlAction(0, 257, true) -- disable melee - DisableControlAction(0, 140, true) -- disable melee - DisableControlAction(0, 141, true) -- disable melee - DisableControlAction(0, 143, true) -- disable melee - DisableControlAction(0, 75, true) -- disable exit vehicle - DisableControlAction(27, 75, true) -- disable exit vehicle - else - Citizen.Wait(500) - end - end -end) - -function verifyName(name) - -- Don't allow short user names - local nameLength = string.len(name) - if nameLength > 25 or nameLength < 2 then - return 'Your player name is either too short or too long.' - end - - -- Don't allow special characters (doesn't always work) - local count = 0 - for i in name:gmatch('[abcdefghijklmnopqrstuvwxyzåäöABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ0123456789 -]') do - count = count + 1 - end - if count ~= nameLength then - return 'Your player name contains special characters that are not allowed on this server.' - end - - -- Does the player carry a first and last name? - -- - -- Example: - -- Allowed: 'Bob Joe' - -- Not allowed: 'Bob' - -- Not allowed: 'Bob joe' - local spacesInName = 0 - local spacesWithUpper = 0 - for word in string.gmatch(name, '%S+') do - - if string.match(word, '%u') then - spacesWithUpper = spacesWithUpper + 1 - end - - spacesInName = spacesInName + 1 - end - - if spacesInName > 2 then - return 'Your name contains more than two spaces' - end - - if spacesWithUpper ~= spacesInName then - return 'your name must start with a capital letter.' - end - - return '' + end) end diff --git a/config.lua b/config.lua index aaec53d7..3d37568a 100644 --- a/config.lua +++ b/config.lua @@ -1,3 +1,12 @@ -Config = {} +Config = {} +Config.Locale = 'en' +Config.EnableCommands = true +Config.UseSteamID = false +Config.UseDeferrals = true +Config.MaxNameLength = 16 +Config.MinHeight = 48 +Config.MaxHeight = 96 +Config.LowestYear = 1900 +Config.HighestYear = 2020 -Config.Locale = 'en' \ No newline at end of file +Config.EnableDebugging = false \ No newline at end of file diff --git a/esx_identity.sql b/esx_identity.sql index e6a39cc3..c9e411d5 100644 --- a/esx_identity.sql +++ b/esx_identity.sql @@ -1,9 +1,9 @@ -USE `essentialmode`; +USE `es_extended`; ALTER TABLE `users` - ADD COLUMN `firstname` VARCHAR(50) NULL DEFAULT '', - ADD COLUMN `lastname` VARCHAR(50) NULL DEFAULT '', - ADD COLUMN `dateofbirth` VARCHAR(25) NULL DEFAULT '', - ADD COLUMN `sex` VARCHAR(10) NULL DEFAULT '', - ADD COLUMN `height` VARCHAR(5) NULL DEFAULT '' + ADD COLUMN `firstname` VARCHAR(16) NULL DEFAULT NULL, + ADD COLUMN `lastname` VARCHAR(16) NULL DEFAULT NULL, + ADD COLUMN `dateofbirth` VARCHAR(10) NULL DEFAULT NULL, + ADD COLUMN `sex` VARCHAR(1) NULL DEFAULT NULL, + ADD COLUMN `height` INT(2) NULL DEFAULT NULL ; \ No newline at end of file diff --git a/fxmanifest.lua b/fxmanifest.lua index 256ab072..09853b6a 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -4,7 +4,7 @@ game 'gta5' description 'ESX Identity' -version '1.2.0' +version '1.4.0' server_scripts { '@es_extended/locale.lua', @@ -27,8 +27,9 @@ ui_page 'html/index.html' files { 'html/index.html', - 'html/script.js', - 'html/style.css' + 'html/js/script.js', + 'html/css/style.css', + 'html/img/esx_identity.png' } dependency 'es_extended' diff --git a/html/css/style.css b/html/css/style.css new file mode 100644 index 00000000..eaa2535e --- /dev/null +++ b/html/css/style.css @@ -0,0 +1,127 @@ +body { + font-family: sans-serif; + overflow: hidden; + display: none; + color: #000000; +} + +.dialog { + width: 332px; + opacity : 0.95; + position : absolute; + margin-left: auto; + margin-right: auto; + top : 30.0%; + padding: 10px; + left : 50%; /* à 50%/50% du parent référent */ + transform : translate(-50%); /* décalage de 50% de sa propre taille */ + background-color: #B7B7B7; + border-radius : 4px; + box-shadow: 0px 0px 50px 0px #000; + border-left:5px solid #000; + border:1px inset; + margin:5px; + margin-bottom:20px; + color: #000000; +} + +input { + width: 100%; + padding: 10px; + text-align:left; + color: #000000; +} + +::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ + color: black; + opacity: 1; /* Firefox */ +} + +.radio-toolbar input[type="radio"] { + opacity: 0; + position: absolute; + width: 36%; +} + +.radio-toolbar label { + display: inline-block; + margin-top: 5px; + background-color: #ddd; + padding: 10px 20px; + font-family: sans-serif, Arial; + font-size: 16px; + color: #000000; + border: 2px solid #444; + border-radius: 4px; + width: 36%; +} + +.radio-toolbar input[type="radio"]:checked + label { + width: 36%; + background-color:#3399FF; + border-color: #0033FF; + color: #000000; +} + +.radio-toolbar input[type="radio"]:focus + label { + border: 2px solid #444; + width: 36%; + color: #000000; +} + +.radio-toolbar label:hover { + background-color: #dfd; + width: 36%; + color: #000000; +} + +button { + display: block; + margin-top: 5px; + padding: 10px; + background-color: #373737; + border: 2px solid #444; + color: #FFFFFF; + width: 100%; +} + +h1 { + display: block; + margin-top: 5px; + margin-right: 5px; + padding: 10px; + background-color: #506be6; + color: #000000; + width: 93%; + text-align: center; +} + +.range-wrap { + position: relative; + margin: 0 auto 3rem; +} + +.range { + width: 100%; +} + +.bubble { + background: #3399FF; + color: black; + padding: 4px 12px; + position: absolute; + border-radius: 4px; + left: 50%; + transform: translateX(-50%); +} + +.bubble::after { + content: ""; + position: absolute; + width: 2px; + height: 2px; + background: #3399FF; + color: black; + top: -1px; + left: 50%; +} \ No newline at end of file diff --git a/html/img/esx_identity.png b/html/img/esx_identity.png new file mode 100644 index 00000000..dcfc17c4 Binary files /dev/null and b/html/img/esx_identity.png differ diff --git a/html/index.html b/html/index.html index 6fc5dfc7..182d344a 100644 --- a/html/index.html +++ b/html/index.html @@ -1,22 +1,163 @@ - + + + - +
-
-

Register

-
-
-
-
- Male - Female
- + + +
+
+
+
+
+ + + + + +
+
+
+ + +
+
+
If the submit button doesn't work, please ensure that you've entered the fields correctly.
- + + diff --git a/html/index_heightslider.html b/html/index_heightslider.html new file mode 100644 index 00000000..182d344a --- /dev/null +++ b/html/index_heightslider.html @@ -0,0 +1,163 @@ + + + + + + + + + +
+ +
+
+
+
+
+
+ + + + + +
+
+
+ + +
+ +
+
If the submit button doesn't work, please ensure that you've entered the fields correctly.
+
+ + + + diff --git a/html/index_heighttext.html b/html/index_heighttext.html new file mode 100644 index 00000000..13896e58 --- /dev/null +++ b/html/index_heighttext.html @@ -0,0 +1,150 @@ + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + +
+
+ +
+
If the submit button doesn't work, please ensure that you've entered the fields correctly.
+
+ + + + diff --git a/html/index_options/index_heightslider.html b/html/index_options/index_heightslider.html new file mode 100644 index 00000000..182d344a --- /dev/null +++ b/html/index_options/index_heightslider.html @@ -0,0 +1,163 @@ + + + + + + + + + +
+ +
+
+
+
+
+
+ + + + + +
+
+
+ + +
+ +
+
If the submit button doesn't work, please ensure that you've entered the fields correctly.
+
+ + + + diff --git a/html/index_options/index_heighttext.html b/html/index_options/index_heighttext.html new file mode 100644 index 00000000..13896e58 --- /dev/null +++ b/html/index_options/index_heighttext.html @@ -0,0 +1,150 @@ + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + +
+
+ +
+
If the submit button doesn't work, please ensure that you've entered the fields correctly.
+
+ + + + diff --git a/html/js/script.js b/html/js/script.js new file mode 100644 index 00000000..a6a5afce --- /dev/null +++ b/html/js/script.js @@ -0,0 +1,34 @@ +$(function() { + window.addEventListener('message', function(event) { + if (event.data.type == "enableui") { + document.body.style.display = event.data.enable ? "block" : "none"; + } + }); + + $("#register").submit(function(event) { + event.preventDefault(); // Prevent form from submitting + + // Verify date + var date = $("#dateofbirth").val(); + var dateCheck = new Date($("#dateofbirth").val()); + + if (dateCheck == "Invalid Date") { + date == "invalid"; + } + else { + const ye = new Intl.DateTimeFormat('en', { year: 'numeric' }).format(dateCheck) + const mo = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(dateCheck) + const da = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(dateCheck) + + var formattedDate = `${mo}/${da}/${ye}`; + + $.post('http://esx_identity/register', JSON.stringify({ + firstname: $("#firstname").val(), + lastname: $("#lastname").val(), + dateofbirth: formattedDate, + sex: $("input[type='radio'][name='sex']:checked").val(), + height: $("#height").val() + })); + } + }); +}); diff --git a/html/cs_index.html b/html/localization/cs_index(outofdate).html similarity index 100% rename from html/cs_index.html rename to html/localization/cs_index(outofdate).html diff --git a/html/fi_index.html b/html/localization/fi_index(outofdate).html similarity index 100% rename from html/fi_index.html rename to html/localization/fi_index(outofdate).html diff --git a/html/ko_index.html b/html/localization/ko_index(outofdate).html similarity index 100% rename from html/ko_index.html rename to html/localization/ko_index(outofdate).html diff --git a/html/sv_index.html b/html/localization/sv_index(outofdate).html similarity index 100% rename from html/sv_index.html rename to html/localization/sv_index(outofdate).html diff --git a/html/script.js b/html/script.js deleted file mode 100644 index b4d4d5b4..00000000 --- a/html/script.js +++ /dev/null @@ -1,33 +0,0 @@ -$(function() { - window.addEventListener('message', function(event) { - if (event.data.type == "enableui") { - document.body.style.display = event.data.enable ? "block" : "none"; - } - }); - - document.onkeyup = function (data) { - if (data.which == 27) { // Escape key - $.post('http://esx_identity/escape', JSON.stringify({})); - } - }; - - $("#register").submit(function(event) { - event.preventDefault(); // Prevent form from submitting - - // Verify date - var date = $("#dateofbirth").val(); - var dateCheck = new Date($("#dateofbirth").val()); - - if (dateCheck == "Invalid Date") { - date == "invalid"; - } - - $.post('http://esx_identity/register', JSON.stringify({ - firstname: $("#firstname").val(), - lastname: $("#lastname").val(), - dateofbirth: date, - sex: $("input[type='radio'][name='sex']:checked").val(), - height: $("#height").val() - })); - }); -}); diff --git a/html/style.css b/html/style.css deleted file mode 100644 index 79a6b347..00000000 --- a/html/style.css +++ /dev/null @@ -1,46 +0,0 @@ -body { - font-family: sans-serif; - overflow: hidden; - display: none; -} - -.dialog { - width: 300px; - position: absolute; - margin-left: auto; - margin-right: auto; - left: 0; - right: 0; - padding: 10px; - box-shadow: 0px 0px 50px 0px #000; - background-color: #f1f1f1; - overflow: hidden; - opacity: 0.9; -} - -input[type=text] { - width: 100%; - padding: 7px; - text-align: center; -} - -button { - display: block; - margin-top: 5px; - padding: 10px; - background-color: #506be6; - border: 0; - color: #fff; - width: 100%; -} - -h1 { - display: block; - margin-top: 5px; - margin-right: 5px; - padding: 10px; - background-color: #506be6; - color: #fff; - width: 93%; - text-align: center; -} \ No newline at end of file diff --git a/locales/en.lua b/locales/en.lua index 53fac161..165ee7ae 100644 --- a/locales/en.lua +++ b/locales/en.lua @@ -1,11 +1,33 @@ Locales['en'] = { - ['show_registration'] = 'show Registration Menu', - ['show_active_character'] = 'show Active Character', - ['delete_character'] = 'delete Your Character And Create A New One', - ['deleted_character'] = 'your character has been deleted.', - ['not_registered'] = 'you do not have a character reistered.', - ['active_character'] = '~b~active Character:~s~ %s %s', - ['already_registered'] = 'you already have a character registered.', - ['failed_identity'] = 'failed to set your character, try again later or contact the server admin!', - ['create_a_character'] = 'you have to create a character in order to play.' + ['show_active_character'] = 'Show Active Character', + ['active_character'] = 'Active Character: ~b~%s~s~', + ['error_active_character'] = "There was an error retrieving your name. Please contact an admin.", + ['delete_character'] = 'Delete Your Character And Create A New One', + ['deleted_character'] = 'Your character has been deleted.', + ['error_delete_character'] = "There was an error deleting your character. Please contact an admin.", + ['thank_you_for_registering'] = "Thank you for registering. Have fun.", + ['registration_error'] = "There was an error during registration. Please contact an admin.", + ['debug_xPlayer_get_first_name'] = "Return Your First Name", + ['debug_xPlayer_get_last_name'] = "Return Your Last Name", + ['debug_xPlayer_get_full_name'] = "Return Your Full Name", + ['debug_xPlayer_get_sex'] = "Return Your Sex", + ['debug_xPlayer_get_dob'] = "Return Your DOB", + ['debug_xPlayer_get_height'] = "Return Your Height", + ['error_debug_xPlayer_get_first_name'] = "There was an error retrieving your first name.", + ['error_debug_xPlayer_get_last_name'] = "There was an error retrieving your last name.", + ['error_debug_xPlayer_get_full_name'] = "There was an error retrieving your full name.", + ['error_debug_xPlayer_get_sex'] = "There was an error retrieving your sex.", + ['error_debug_xPlayer_get_dob'] = "There was an error retrieving your date of birth.", + ['error_debug_xPlayer_get_height'] = "There was an error retrieving your height.", + ['return_debug_xPlayer_get_first_name'] = "First Name: ~b~%s", + ['return_debug_xPlayer_get_last_name'] = "Last Name: ~b~%s", + ['return_debug_xPlayer_get_full_name'] = "Full Name: ~b~%s", + ['return_debug_xPlayer_get_sex'] = "Sex: ~b~%s", + ['return_debug_xPlayer_get_dob'] = "DOB: ~b~%s", + ['return_debug_xPlayer_get_height'] = "Height: ~b~%s Inches", + ['data_incorrect'] = 'Data entered incorrectly. Please try again.', + ['invalid_format'] = 'Data formatted incorrectly. Please try again.', + ['no_identifier'] = '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.', + ['missing_identity'] = 'There was an error loading your character!\nError code: identity-missing\n\nIt seems like your identity is missing, try connecting again.', + ['deleted_identity'] = 'Your character has been deleted. Please login again in order to create a new character.' } diff --git a/server/main.lua b/server/main.lua index f96f4495..ede6b5bd 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,189 +1,557 @@ ESX = nil - +local playerIdentity = {} +local alreadyRegistered = {} TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) -function getIdentity(source, callback) - local xPlayer = ESX.GetPlayerFromId(source) - - MySQL.Async.fetchAll('SELECT identifier, firstname, lastname, dateofbirth, sex, height FROM `users` WHERE `identifier` = @identifier', { - ['@identifier'] = xPlayer.identifier - }, function(result) - if result[1].firstname ~= nil then - local data = { - identifier = result[1].identifier, - firstname = result[1].firstname, - lastname = result[1].lastname, - dateofbirth = result[1].dateofbirth, - sex = result[1].sex, - height = result[1].height - } - - callback(data) +if Config.UseDeferrals then + AddEventHandler('playerConnecting', function(playerName, setKickReason, deferrals) + deferrals.defer() + local playerId, identifier = source + Citizen.Wait(100) + + if Config.UseSteamID then + for k,v in ipairs(GetPlayerIdentifiers(playerId)) do + if string.match(v, 'steam:') then + identifier = v + break + end + end else - local data = { - identifier = '', - firstname = '', - lastname = '', - dateofbirth = '', - sex = '', - height = '' - } - - callback(data) + for k,v in ipairs(GetPlayerIdentifiers(playerId)) do + if string.match(v, 'license:') then + identifier = string.sub(v, 9) + break + end + end + end + + 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 + playerIdentity[identifier] = { + firstName = result[1].firstname, + lastName = result[1].lastname, + dateOfBirth = result[1].dateofbirth, + sex = result[1].sex, + height = result[1].height + } + + deferrals.done() + else + deferrals.presentCard([==[{"type": "AdaptiveCard","body":[{"type":"Container","items":[{"type":"ColumnSet","columns":[{"type":"Column","items":[{"type":"Input.Text","placeholder":"First Name","id":"firstname","maxLength":15},{"type":"Input.Text","placeholder":"Date of Birth (MM/DD/YYYY)","id":"dateofbirth","maxLength":10}],"width":"stretch"},{"type":"Column","width":"stretch","items":[{"type":"Input.Text","placeholder":"Last Name","id":"lastname","maxLength":15},{"type":"Input.Text","placeholder":"Height (48-96 inches)","id":"height","maxLength":2}]}]},{"type":"Input.ChoiceSet","placeholder":"Sex","choices":[{"title":"Male","value":"m"},{"title":"Female","value":"f"}],"style":"expanded","id":"sex"}]},{"type": "ActionSet","actions": [{"type":"Action.Submit","title":"Submit"}]}],"$schema": "http://adaptivecards.io/schemas/adaptive-card.json","version":"1.0"}]==], function(data, rawData) + if data.firstname == '' or data.lastname == '' or data.dateofbirth == '' or data.sex == '' or data.height == '' then + deferrals.done(_U('data_incorrect')) + else + if checkNameFormat(data.firstname) and checkNameFormat(data.lastname) and checkDOBFormat(data.dateofbirth) and checkSexFormat(data.sex) and checkHeightFormat(data.height) then + playerIdentity[identifier] = { + firstName = formatName(data.firstname), + lastName = formatName(data.lastname), + dateOfBirth = data.dateofbirth, + sex = data.sex, + height = tonumber(data.height), + saveToDatabase = true + } + + deferrals.done() + else + deferrals.done(_U('invalid_format')) + end + end + end) + end + else + deferrals.presentCard([==[{"type": "AdaptiveCard","body":[{"type":"Container","items":[{"type":"ColumnSet","columns":[{"type":"Column","items":[{"type":"Input.Text","placeholder":"First Name","id":"firstname","maxLength":15},{"type":"Input.Text","placeholder":"Date of Birth (MM/DD/YYYY)","id":"dateofbirth","maxLength":10}],"width":"stretch"},{"type":"Column","width":"stretch","items":[{"type":"Input.Text","placeholder":"Last Name","id":"lastname","maxLength":15},{"type":"Input.Text","placeholder":"Height (48-96 inches)","id":"height","maxLength":2}]}]},{"type":"Input.ChoiceSet","placeholder":"Sex","choices":[{"title":"Male","value":"m"},{"title":"Female","value":"f"}],"style":"expanded","id":"sex"}]},{"type": "ActionSet","actions": [{"type":"Action.Submit","title":"Submit"}]}],"$schema": "http://adaptivecards.io/schemas/adaptive-card.json","version":"1.0"}]==], function(data, rawData) + if data.firstname == '' or data.lastname == '' or data.dateofbirth == '' or data.sex == '' or data.height == '' then + deferrals.done(_U('data_incorrect')) + else + if checkNameFormat(data.firstname) and checkNameFormat(data.lastname) and checkDOBFormat(data.dateofbirth) and checkSexFormat(data.sex) and checkHeightFormat(data.height) then + playerIdentity[identifier] = { + firstName = formatName(data.firstname), + lastName = formatName(data.lastname), + dateOfBirth = data.dateofbirth, + sex = data.sex, + height = tonumber(data.height), + saveToDatabase = true + } + + deferrals.done() + else + deferrals.done(_U('invalid_format')) + end + end + end) + end + end) + else + deferrals.done(_U('no_identifier')) end end) -end + + RegisterNetEvent('esx:playerLoaded') + AddEventHandler('esx:playerLoaded', function(playerId, xPlayer) + if playerIdentity[xPlayer.identifier] then + local currentIdentity = playerIdentity[xPlayer.identifier] + + xPlayer.setName(('%s %s'):format(currentIdentity.firstName, currentIdentity.lastName)) + xPlayer.set('firstName', currentIdentity.firstName) + xPlayer.set('lastName', currentIdentity.lastName) + xPlayer.set('dateofbirth', currentIdentity.dateOfBirth) + xPlayer.set('sex', currentIdentity.sex) + xPlayer.set('height', currentIdentity.height) + + if currentIdentity.saveToDatabase then + saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + end -function setIdentity(identifier, data, callback) - MySQL.Async.execute('UPDATE `users` SET `firstname` = @firstname, `lastname` = @lastname, `dateofbirth` = @dateofbirth, `sex` = @sex, `height` = @height WHERE identifier = @identifier', { - ['@identifier'] = identifier, - ['@firstname'] = data.firstname, - ['@lastname'] = data.lastname, - ['@dateofbirth'] = data.dateofbirth, - ['@sex'] = data.sex, - ['@height'] = data.height - }, function(rowsChanged) - if callback then - callback(true) + Citizen.Wait(1000) + alreadyRegistered[xPlayer.identifier] = true + TriggerClientEvent('esx_identity:alreadyRegistered', xPlayer.source) + + playerIdentity[xPlayer.identifier] = nil + else + xPlayer.kick(_('missing_identity')) end end) -end +elseif not Config.UseDeferrals then + AddEventHandler('playerConnecting', function(playerName, setKickReason, deferrals) + deferrals.defer() + local playerId, identifier = source + Citizen.Wait(100) -function updateIdentity(playerId, data, callback) - local xPlayer = ESX.GetPlayerFromId(playerId) + if Config.UseSteamID then + for k,v in ipairs(GetPlayerIdentifiers(playerId)) do + if string.match(v, 'steam:') then + identifier = v + break + end + end + else + for k,v in ipairs(GetPlayerIdentifiers(playerId)) do + if string.match(v, 'license:') then + identifier = string.sub(v, 9) + break + end + end + end - MySQL.Async.execute('UPDATE `users` SET `firstname` = @firstname, `lastname` = @lastname, `dateofbirth` = @dateofbirth, `sex` = @sex, `height` = @height WHERE identifier = @identifier', { - ['@identifier'] = xPlayer.identifier, - ['@firstname'] = data.firstname, - ['@lastname'] = data.lastname, - ['@dateofbirth'] = data.dateofbirth, - ['@sex'] = data.sex, - ['@height'] = data.height - }, function(rowsChanged) - if callback then - TriggerEvent('esx_identity:characterUpdated', playerId, data) - callback(true) + 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 + playerIdentity[identifier] = { + firstName = result[1].firstname, + lastName = result[1].lastname, + dateOfBirth = result[1].dateofbirth, + sex = result[1].sex, + height = result[1].height + } + + alreadyRegistered[identifier] = true + + deferrals.done() + else + playerIdentity[identifier] = nil + alreadyRegistered[identifier] = false + deferrals.done() + end + else + playerIdentity[identifier] = nil + alreadyRegistered[identifier] = false + deferrals.done() + end + end) + else + deferrals.done(_U('no_identifier')) end end) + + AddEventHandler('onResourceStart', function(resource) + if resource == GetCurrentResourceName() then + Citizen.Wait(1000) + + while not ESX do + Citizen.Wait(10) + end + + local xPlayers = ESX.GetPlayers() + + for i=1, #xPlayers, 1 do + local xPlayer = ESX.GetPlayerFromId(xPlayers[i]) + + if xPlayer then + checkIdentity(xPlayer) + end + end + end + end) + + RegisterNetEvent('esx:playerLoaded') + AddEventHandler('esx:playerLoaded', function(playerId, xPlayer) + if alreadyRegistered[xPlayer.identifier] == true then + local currentIdentity = playerIdentity[xPlayer.identifier] + + xPlayer.setName(('%s %s'):format(currentIdentity.firstName, currentIdentity.lastName)) + xPlayer.set('firstName', currentIdentity.firstName) + xPlayer.set('lastName', currentIdentity.lastName) + xPlayer.set('dateofbirth', currentIdentity.dateOfBirth) + xPlayer.set('sex', currentIdentity.sex) + xPlayer.set('height', currentIdentity.height) + + if currentIdentity.saveToDatabase then + saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + end + + Citizen.Wait(1000) + TriggerClientEvent('esx_identity:alreadyRegistered', xPlayer.source) + + playerIdentity[xPlayer.identifier] = nil + else + TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) + end + end) + + ESX.RegisterServerCallback('esx_identity:registerIdentity', function(source, cb, data) + local xPlayer = ESX.GetPlayerFromId(source) + + if xPlayer then + if not alreadyRegistered[xPlayer.identifier] then + if checkNameFormat(data.firstname) and checkNameFormat(data.lastname) and checkSexFormat(data.sex) and checkDOBFormat(data.dateofbirth) and checkHeightFormat(data.height) then + playerIdentity[xPlayer.identifier] = { + firstName = formatName(data.firstname), + lastName = formatName(data.lastname), + dateOfBirth = data.dateofbirth, + sex = data.sex, + height = data.height + } + + local currentIdentity = playerIdentity[xPlayer.identifier] + + xPlayer.setName(('%s %s'):format(currentIdentity.firstName, currentIdentity.lastName)) + xPlayer.set('firstName', currentIdentity.firstName) + xPlayer.set('lastName', currentIdentity.lastName) + xPlayer.set('dateofbirth', currentIdentity.dateOfBirth) + xPlayer.set('sex', currentIdentity.sex) + xPlayer.set('height', currentIdentity.height) + + saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + alreadyRegistered[xPlayer.identifier] = true + + playerIdentity[xPlayer.identifier] = nil + cb(true) + else + cb(false) + end + else + cb(false) + end + end + 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 + playerIdentity[xPlayer.identifier] = { + firstName = result[1].firstname, + lastName = result[1].lastname, + dateOfBirth = result[1].dateofbirth, + sex = result[1].sex, + height = result[1].height + } + + alreadyRegistered[xPlayer.identifier] = true + + setIdentity(xPlayer) + else + playerIdentity[xPlayer.identifier] = nil + alreadyRegistered[xPlayer.identifier] = false + TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) + end + else + TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) + end + end) + end + + function setIdentity(xPlayer) + if alreadyRegistered[xPlayer.identifier] then + local currentIdentity = playerIdentity[xPlayer.identifier] + + xPlayer.setName(('%s %s'):format(currentIdentity.firstName, currentIdentity.lastName)) + xPlayer.set('firstName', currentIdentity.firstName) + xPlayer.set('lastName', currentIdentity.lastName) + xPlayer.set('dateofbirth', currentIdentity.dateOfBirth) + xPlayer.set('sex', currentIdentity.sex) + xPlayer.set('height', currentIdentity.height) + + if currentIdentity.saveToDatabase then + saveIdentityToDatabase(xPlayer.identifier, currentIdentity) + end + + playerIdentity[xPlayer.identifier] = nil + end + end end -function deleteIdentity(source) - local xPlayer = ESX.GetPlayerFromId(source) +if Config.EnableCommands then + ESX.RegisterCommand('char', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.getName() then + xPlayer.showNotification(_U('active_character', xPlayer.getName())) + else + xPlayer.showNotification(_U('error_active_character')) + end + end, false, {help = _U('show_active_character')}) - MySQL.Async.execute('UPDATE `users` SET `firstname` = @firstname, `lastname` = @lastname, `dateofbirth` = @dateofbirth, `sex` = @sex, `height` = @height WHERE identifier = @identifier', { - ['@identifier'] = xPlayer.identifier, - ['@firstname'] = '', - ['@lastname'] = '', - ['@dateofbirth'] = '', - ['@sex'] = '', - ['@height'] = '', + ESX.RegisterCommand('chardel', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.getName() then + if Config.UseDeferrals then + deleteIdentity(xPlayer) + xPlayer.showNotification(_U('deleted_character')) + playerIdentity[xPlayer.identifier] = nil + alreadyRegistered[xPlayer.identifier] = false + Citizen.Wait(2500) + xPlayer.kick(_('deleted_identity')) + else + deleteIdentity(xPlayer) + xPlayer.showNotification(_U('deleted_character')) + playerIdentity[xPlayer.identifier] = nil + alreadyRegistered[xPlayer.identifier] = false + TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) + end + else + xPlayer.showNotification(_U('error_delete_character')) + end + end, false, {help = _U('delete_character')}) +end + +if Config.EnableDebugging then + ESX.RegisterCommand('xPlayerGetFirstName', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.get('firstName') then + xPlayer.showNotification(_U('return_debug_xPlayer_get_first_name', xPlayer.get('firstName'))) + else + xPlayer.showNotification(_U('error_debug_xPlayer_get_first_name')) + end + end, false, {help = _U('debug_xPlayer_get_first_name')}) + + ESX.RegisterCommand('xPlayerGetLastName', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.get('lastName') then + xPlayer.showNotification(_U('return_debug_xPlayer_get_last_name', xPlayer.get('lastName'))) + else + xPlayer.showNotification(_U('error_debug_xPlayer_get_last_name')) + end + end, false, {help = _U('debug_xPlayer_get_last_name')}) + + ESX.RegisterCommand('xPlayerGetFullName', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.getName() then + xPlayer.showNotification(_U('return_debug_xPlayer_get_full_name', xPlayer.getName())) + else + xPlayer.showNotification(_U('error_debug_xPlayer_get_full_name')) + end + end, false, {help = _U('debug_xPlayer_get_full_name')}) + + ESX.RegisterCommand('xPlayerGetSex', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.get('sex') then + xPlayer.showNotification(_U('return_debug_xPlayer_get_sex', xPlayer.get('sex'))) + else + xPlayer.showNotification(_U('error_debug_xPlayer_get_sex')) + end + end, false, {help = _U('debug_xPlayer_get_sex')}) + + ESX.RegisterCommand('xPlayerGetDOB', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.get('dateofbirth') then + xPlayer.showNotification(_U('return_debug_xPlayer_get_dob', xPlayer.get('dateofbirth'))) + else + xPlayer.showNotification(_U('error_debug_xPlayer_get_dob')) + end + end, false, {help = _U('debug_xPlayer_get_dob')}) + + ESX.RegisterCommand('xPlayerGetHeight', 'user', function(xPlayer, args, showError) + if xPlayer and xPlayer.get('height') then + xPlayer.showNotification(_U('return_debug_xPlayer_get_height', xPlayer.get('height'))) + else + xPlayer.showNotification(_U('error_debug_xPlayer_get_height')) + end + end, false, {help = _U('debug_xPlayer_get_height')}) +end + +function deleteIdentity(xPlayer) + if alreadyRegistered[xPlayer.identifier] then + xPlayer.setName(('%s %s'):format(nil, nil)) + xPlayer.set('firstName', nil) + xPlayer.set('lastName', nil) + xPlayer.set('dateofbirth', nil) + xPlayer.set('sex', nil) + xPlayer.set('height', nil) + + deleteIdentityFromDatabase(xPlayer) + end +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 }) end -RegisterServerEvent('esx_identity:setIdentity') -AddEventHandler('esx_identity:setIdentity', function(data, myIdentifiers) - local xPlayer = ESX.GetPlayerFromId(source) - setIdentity(myIdentifiers.steamid, data, function(callback) - if callback then - TriggerClientEvent('esx_identity:identityCheck', myIdentifiers.playerid, true) - TriggerEvent('esx_identity:characterUpdated', myIdentifiers.playerid, data) +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.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.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 + }) +end + +function checkNameFormat(name) + if not checkAlphanumeric(name) then + if not checkForNumbers(name) then + local stringLength = string.len(name) + if stringLength > 0 and stringLength < Config.MaxNameLength then + return true + else + return false + end else - xPlayer.showNotification(_U('failed_identity')) + return false end - end) -end) - -AddEventHandler('esx:playerLoaded', function(playerId, xPlayer) - local myID = { - steamid = xPlayer.identifier, - playerid = playerId - } - - TriggerClientEvent('esx_identity:saveID', playerId, myID) - - getIdentity(playerId, function(data) - if data.firstname == '' then - TriggerClientEvent('esx_identity:identityCheck', playerId, false) - TriggerClientEvent('esx_identity:showRegisterIdentity', playerId) - else - TriggerClientEvent('esx_identity:identityCheck', playerId, true) - TriggerEvent('esx_identity:characterUpdated', playerId, data) - end - end) -end) - -AddEventHandler('esx_identity:characterUpdated', function(playerId, data) - local xPlayer = ESX.GetPlayerFromId(playerId) - - if xPlayer then - xPlayer.setName(('%s %s'):format(data.firstname, data.lastname)) - xPlayer.set('firstName', data.firstname) - xPlayer.set('lastName', data.lastname) - xPlayer.set('dateofbirth', data.dateofbirth) - xPlayer.set('sex', data.sex) - xPlayer.set('height', data.height) + else + return false end -end) +end --- Set all the client side variables for connected users one new time -AddEventHandler('onResourceStart', function(resource) - if resource == GetCurrentResourceName() then - Citizen.Wait(3000) - local xPlayers = ESX.GetPlayers() +function checkDOBFormat(dob) + local date = tostring(dob) + if checkDate(date) then + return true + else + return false + end +end - for i=1, #xPlayers, 1 do - local xPlayer = ESX.GetPlayerFromId(xPlayers[i]) +function checkSexFormat(sex) + if sex == "m" or sex == "M" or sex == "f" or sex == "F" then + return true + else + return false + end +end - if xPlayer then - local myID = { - steamid = xPlayer.identifier, - playerid = xPlayer.source - } - - TriggerClientEvent('esx_identity:saveID', xPlayer.source, myID) - - getIdentity(xPlayer.source, function(data) - if data.firstname == '' then - TriggerClientEvent('esx_identity:identityCheck', xPlayer.source, false) - TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) - else - TriggerClientEvent('esx_identity:identityCheck', xPlayer.source, true) - TriggerEvent('esx_identity:characterUpdated', xPlayer.source, data) - end - end) +function checkHeightFormat(height) + local numHeight = tonumber(height) + if numHeight < Config.MinHeight and numHeight > Config.MaxHeight then + return false + else + return true + end +end + +function formatName(name) + local loweredName = convertToLowerCase(name) + local formattedName = convertFirstLetterToUpper(loweredName) + return formattedName +end + +function convertToLowerCase(str) + return string.lower(str) +end + +function convertFirstLetterToUpper(str) + return str:gsub("^%l", string.upper) +end + +function checkAlphanumeric(str) + return (string.match(str, "%W")) +end + +function checkForNumbers(str) + return (string.match(str,"%d")) +end + +function checkDate(str) + if string.match(str, '(%d%d)/(%d%d)/(%d%d%d%d)') ~= nil then + local m, d, y = string.match(str, '(%d+)/(%d+)/(%d+)') + m = tonumber(m) + d = tonumber(d) + y = tonumber(y) + if ((d <= 0) or (d > 31)) or ((m <= 0) or (m > 12)) or ((y <= Config.LowestYear) or (y > Config.HighestYear)) then + return false + elseif m == 4 or m == 6 or m == 9 or m == 11 then + if d > 30 then + return false + else + return true + end + elseif m == 2 then + if y%400 == 0 or (y%100 ~= 0 and y%4 == 0) then + if d > 29 then + return false + else + return true + end + else + if d > 28 then + return false + else + return true + end + end + else + if d > 31 then + return false + else + return true end end + else + return false end -end) - -ESX.RegisterCommand('register', 'user', function(xPlayer, args, showError) - getIdentity(xPlayer.source, function(data) - if data.firstname ~= '' then - xPlayer.showNotification(_U('already_registered')) - else - TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) - end - end) -end, false, {help = _U('show_registration')}) - -ESX.RegisterCommand('char', 'user', function(xPlayer, args, showError) - getIdentity(xPlayer.source, function(data) - if data.firstname == '' then - xPlayer.showNotification(_U('not_registered')) - else - xPlayer.showNotification(_U('active_character', data.firstname, data.lastname)) - end - end) -end, false, {help = _U('show_active_character')}) - -ESX.RegisterCommand('chardel', 'user', function(xPlayer, args, showError) - getIdentity(xPlayer.source, function(data) - if data.firstname == '' then - xPlayer.showNotification(_U('not_registered')) - else - deleteIdentity(xPlayer.source) - xPlayer.showNotification(_U('deleted_character')) - TriggerClientEvent('esx_identity:showRegisterIdentity', xPlayer.source) - end - end) -end, false, {help = _U('delete_character')}) \ No newline at end of file +end