Refactor core structure and notification UI

Major refactor to centralize QBCore and replace the web UI stack:

- Move QBConfig into a unified QBCore.Config and initialize QBCore.PlayerData; add shared/functions.lua.
- Remove legacy client/main.lua and server/main.lua exports and update fxmanifest to include shared/functions and drop those main scripts.
- Update client event handling: adapt player data events to QBCore:Client:OnPlayerUpdated and trigger job/gang update events; remove deprecated exploitable handlers.
- Web UI rewrite: remove Vue/Quasar dependency and config module; implement a lightweight vanilla JS notification system (html/js/app.js) with updated html and css assets, plus drawtext tweaks.
- CSS overhaul for notification styling and animations; HTML head/meta/font cleanup.
- Server fixes: use QBCore.Shared.Locations in teleport command and strengthen numeric parsing/validation for coords.
- Add .gitattributes for consistent text/eol handling and small code cleanups (thread/comment removal).

These changes consolidate config/shared logic under QBCore, simplify the frontend dependencies, and fix a few parsing/event issues.
This commit is contained in:
Kakarot
2026-05-19 18:13:35 -05:00
parent 891b039bf3
commit 74ac036d4a
28 changed files with 1242 additions and 1419 deletions
+172
View File
@@ -0,0 +1,172 @@
local StringCharset = {}
local NumberCharset = {}
QBCore.Shared.StarterItems = {
['phone'] = { amount = 1, item = 'phone' },
['id_card'] = { amount = 1, item = 'id_card' },
['driver_license'] = { amount = 1, item = 'driver_license' },
}
for i = 48, 57 do NumberCharset[#NumberCharset + 1] = string.char(i) end
for i = 65, 90 do StringCharset[#StringCharset + 1] = string.char(i) end
for i = 97, 122 do StringCharset[#StringCharset + 1] = string.char(i) end
function QBCore.Shared.RandomStr(length)
if length <= 0 then return '' end
return QBCore.Shared.RandomStr(length - 1) .. StringCharset[math.random(1, #StringCharset)]
end
function QBCore.Shared.RandomInt(length)
if length <= 0 then return '' end
return QBCore.Shared.RandomInt(length - 1) .. NumberCharset[math.random(1, #NumberCharset)]
end
function QBCore.Shared.SplitStr(str, delimiter)
local result = {}
local from = 1
local delim_from, delim_to = string.find(str, delimiter, from)
while delim_from do
result[#result + 1] = string.sub(str, from, delim_from - 1)
from = delim_to + 1
delim_from, delim_to = string.find(str, delimiter, from)
end
result[#result + 1] = string.sub(str, from)
return result
end
function QBCore.Shared.Trim(value)
if not value then return nil end
return (string.gsub(value, '^%s*(.-)%s*$', '%1'))
end
function QBCore.Shared.FirstToUpper(value)
if not value then return nil end
return (value:gsub('^%l', string.upper))
end
function QBCore.Shared.Round(value, numDecimalPlaces)
if not numDecimalPlaces then return math.floor(value + 0.5) end
local power = 10 ^ numDecimalPlaces
return math.floor((value * power) + 0.5) / (power)
end
function QBCore.Shared.ChangeVehicleExtra(vehicle, extra, enable)
if DoesExtraExist(vehicle, extra) then
if enable then
SetVehicleExtra(vehicle, extra, false)
if not IsVehicleExtraTurnedOn(vehicle, extra) then
QBCore.Shared.ChangeVehicleExtra(vehicle, extra, enable)
end
else
SetVehicleExtra(vehicle, extra, true)
if IsVehicleExtraTurnedOn(vehicle, extra) then
QBCore.Shared.ChangeVehicleExtra(vehicle, extra, enable)
end
end
end
end
function QBCore.Shared.IsFunction(value)
if type(value) == 'table' then
return value.__cfx_functionReference ~= nil and type(value.__cfx_functionReference) == 'string'
end
return type(value) == 'function'
end
function QBCore.Shared.SetDefaultVehicleExtras(vehicle, config)
for i = 1, 20 do
if DoesExtraExist(vehicle, i) then
SetVehicleExtra(vehicle, i, 1)
end
end
for id, enabled in pairs(config) do
if type(enabled) ~= 'boolean' then
enabled = true
end
QBCore.Shared.ChangeVehicleExtra(vehicle, tonumber(id), enabled)
end
end
QBCore.Shared.MaleNoGloves = {
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
[7] = true,
[8] = true,
[9] = true,
[10] = true,
[11] = true,
[12] = true,
[13] = true,
[14] = true,
[15] = true,
[18] = true,
[26] = true,
[52] = true,
[53] = true,
[54] = true,
[55] = true,
[56] = true,
[57] = true,
[58] = true,
[59] = true,
[60] = true,
[61] = true,
[62] = true,
[112] = true,
[113] = true,
[114] = true,
[118] = true,
[125] = true,
[132] = true
}
QBCore.Shared.FemaleNoGloves = {
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
[7] = true,
[8] = true,
[9] = true,
[10] = true,
[11] = true,
[12] = true,
[13] = true,
[14] = true,
[15] = true,
[19] = true,
[59] = true,
[60] = true,
[61] = true,
[62] = true,
[63] = true,
[64] = true,
[65] = true,
[66] = true,
[67] = true,
[68] = true,
[69] = true,
[70] = true,
[71] = true,
[129] = true,
[130] = true,
[131] = true,
[135] = true,
[142] = true,
[149] = true,
[153] = true,
[157] = true,
[161] = true,
[165] = true
}
+1 -2
View File
@@ -1,5 +1,4 @@
QBShared = QBShared or {}
QBShared.Gangs = {
QBCore.Shared.Gangs = {
none = { label = 'No Gang', grades = { ['0'] = { name = 'Unaffiliated' } } },
lostmc = {
label = 'The Lost MC',
+1 -2
View File
@@ -1,5 +1,4 @@
QBShared = QBShared or {}
QBShared.Items = {
QBCore.Shared.Items = {
-- WEAPONS
-- Melee
weapon_unarmed = { name = 'weapon_unarmed', label = 'Fists', weight = 1000, type = 'weapon', ammotype = nil, image = 'placeholder.png', unique = true, useable = false, description = 'Fisticuffs' },
+2 -3
View File
@@ -1,6 +1,5 @@
QBShared = QBShared or {}
QBShared.ForceJobDefaultDutyAtLogin = true -- true: Force duty state to jobdefaultDuty | false: set duty state from database last saved
QBShared.Jobs = {
QBCore.Shared.ForceJobDefaultDutyAtLogin = true -- true: Force duty state to jobdefaultDuty | false: set duty state from database last saved
QBCore.Shared.Jobs = {
unemployed = { label = 'Civilian', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Freelancer', payment = 10 } } },
bus = { label = 'Bus', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Driver', payment = 50 } } },
judge = { label = 'Honorary', defaultDuty = true, offDutyPay = false, grades = { ['0'] = { name = 'Judge', payment = 100 } } },
+1 -1
View File
@@ -1,4 +1,4 @@
QBShared.Locations = {
QBCore.Shared.Locations = {
-- Base game MLO interiors
aircraft_carrier_int = vector4(3081.0042, -4693.6875, 15.2623, 76.8169),
apt_1_int = vector4(-1452.4602, -540.2056, 74.0443, 31.9129),
+17 -174
View File
@@ -1,185 +1,28 @@
QBShared = QBShared or {}
QBCore.Shared = {}
QBCore.ClientCallbacks = {}
QBCore.ServerCallbacks = {}
local StringCharset = {}
local NumberCharset = {}
QBShared.StarterItems = {
['phone'] = { amount = 1, item = 'phone' },
['id_card'] = { amount = 1, item = 'id_card' },
['driver_license'] = { amount = 1, item = 'driver_license' },
}
for i = 48, 57 do NumberCharset[#NumberCharset + 1] = string.char(i) end
for i = 65, 90 do StringCharset[#StringCharset + 1] = string.char(i) end
for i = 97, 122 do StringCharset[#StringCharset + 1] = string.char(i) end
function QBShared.RandomStr(length)
if length <= 0 then return '' end
return QBShared.RandomStr(length - 1) .. StringCharset[math.random(1, #StringCharset)]
end
function QBShared.RandomInt(length)
if length <= 0 then return '' end
return QBShared.RandomInt(length - 1) .. NumberCharset[math.random(1, #NumberCharset)]
end
function QBShared.SplitStr(str, delimiter)
local result = {}
local from = 1
local delim_from, delim_to = string.find(str, delimiter, from)
while delim_from do
result[#result + 1] = string.sub(str, from, delim_from - 1)
from = delim_to + 1
delim_from, delim_to = string.find(str, delimiter, from)
end
result[#result + 1] = string.sub(str, from)
return result
end
function QBShared.Trim(value)
if not value then return nil end
return (string.gsub(value, '^%s*(.-)%s*$', '%1'))
end
function QBShared.FirstToUpper(value)
if not value then return nil end
return (value:gsub("^%l", string.upper))
end
function QBShared.Round(value, numDecimalPlaces)
if not numDecimalPlaces then return math.floor(value + 0.5) end
local power = 10 ^ numDecimalPlaces
return math.floor((value * power) + 0.5) / (power)
end
function QBShared.ChangeVehicleExtra(vehicle, extra, enable)
if DoesExtraExist(vehicle, extra) then
if enable then
SetVehicleExtra(vehicle, extra, false)
if not IsVehicleExtraTurnedOn(vehicle, extra) then
QBShared.ChangeVehicleExtra(vehicle, extra, enable)
end
else
SetVehicleExtra(vehicle, extra, true)
if IsVehicleExtraTurnedOn(vehicle, extra) then
QBShared.ChangeVehicleExtra(vehicle, extra, enable)
end
--- @param filters ('Config'|'Shared'|'ClientCallbacks'|'ServerCallbacks'|'PlayerData'|'Functions'|'Players'|'PlayersByCitizenId'|'Player_Buckets'|'Entity_Buckets'|'UsableItems'|'Commands')[]?
--- @return table
local function GetCoreObject(filters)
if not filters then return QBCore end
local results = {}
for i = 1, #filters do
local key = filters[i]
if QBCore[key] then
results[key] = QBCore[key]
end
end
return results
end
exports('GetCoreObject', GetCoreObject)
function QBShared.IsFunction(value)
if type(value) == 'table' then
return value.__cfx_functionReference ~= nil and type(value.__cfx_functionReference) == "string"
end
return type(value) == 'function'
end
function QBShared.SetDefaultVehicleExtras(vehicle, config)
-- Clear Extras
for i = 1, 20 do
if DoesExtraExist(vehicle, i) then
SetVehicleExtra(vehicle, i, 1)
end
end
for id, enabled in pairs(config) do
if type(enabled) ~= 'boolean' then
enabled = true
end
QBShared.ChangeVehicleExtra(vehicle, tonumber(id), enabled)
end
end
QBShared.MaleNoGloves = {
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
[7] = true,
[8] = true,
[9] = true,
[10] = true,
[11] = true,
[12] = true,
[13] = true,
[14] = true,
[15] = true,
[18] = true,
[26] = true,
[52] = true,
[53] = true,
[54] = true,
[55] = true,
[56] = true,
[57] = true,
[58] = true,
[59] = true,
[60] = true,
[61] = true,
[62] = true,
[112] = true,
[113] = true,
[114] = true,
[118] = true,
[125] = true,
[132] = true
}
QBShared.FemaleNoGloves = {
[0] = true,
[1] = true,
[2] = true,
[3] = true,
[4] = true,
[5] = true,
[6] = true,
[7] = true,
[8] = true,
[9] = true,
[10] = true,
[11] = true,
[12] = true,
[13] = true,
[14] = true,
[15] = true,
[19] = true,
[59] = true,
[60] = true,
[61] = true,
[62] = true,
[63] = true,
[64] = true,
[65] = true,
[66] = true,
[67] = true,
[68] = true,
[69] = true,
[70] = true,
[71] = true,
[129] = true,
[130] = true,
[131] = true,
[135] = true,
[142] = true,
[149] = true,
[153] = true,
[157] = true,
[161] = true,
[165] = true
}
--- Get a shared item from a shared field
--- @param namespace 'Vehicles' | 'VehicleHashes' | 'Items' | 'Gangs' | 'Jobs' | 'Locations' | 'Weapons'
--- @param item string
--- @return table
function GetShared(namespace, item)
return QBCore.Shared[namespace]?[item]
local ns = QBCore.Shared[namespace]
return ns and ns[item]
end
exports('GetShared', GetShared)
exports('GetShared', GetShared)
+4 -5
View File
@@ -1,5 +1,4 @@
QBShared = QBShared or {}
QBShared.Vehicles = QBShared.Vehicles or {}
QBCore.Shared.Vehicles = QBCore.Shared.Vehicles or {}
local Vehicles = {
--- Compacts (0)
@@ -765,10 +764,10 @@ local Vehicles = {
{ model = 'formula', name = 'PR4', brand = 'Progen', price = 100000, category = 'openwheel', type = 'automobile', shop = 'none' },
}
QBShared.VehicleHashes = QBShared.VehicleHashes or {}
QBCore.Shared.VehicleHashes = QBCore.Shared.VehicleHashes or {}
for i = 1, #Vehicles do
local hash = joaat(Vehicles[i].model)
QBShared.Vehicles[Vehicles[i].model] = {
QBCore.Shared.Vehicles[Vehicles[i].model] = {
spawncode = Vehicles[i].model,
name = Vehicles[i].name,
brand = Vehicles[i].brand,
@@ -779,5 +778,5 @@ for i = 1, #Vehicles do
type = Vehicles[i].type,
shop = Vehicles[i].shop
}
QBShared.VehicleHashes[hash] = QBShared.Vehicles[Vehicles[i].model]
QBCore.Shared.VehicleHashes[hash] = QBCore.Shared.Vehicles[Vehicles[i].model]
end
+1 -2
View File
@@ -1,5 +1,4 @@
QBShared = QBShared or {}
QBShared.Weapons = {
QBCore.Shared.Weapons = {
-- // WEAPONS
-- Melee
[`weapon_unarmed`] = { name = 'weapon_unarmed', label = 'Fists', weapontype = 'Melee', ammotype = nil, damagereason = 'Melee killed / Whacked / Executed / Beat down / Murdered / Battered' },