From 36c0ab209a8dcd952c90b329b790d01d486761be Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:31:09 +0200 Subject: [PATCH 001/161] update: old name native --- client/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/main.lua b/client/main.lua index 7038aa9..2d6ccf0 100644 --- a/client/main.lua +++ b/client/main.lua @@ -28,8 +28,8 @@ CreateThread(function() sleep = 0 BeginScaleformMovieMethodOnFrontendHeader("SET_HEADING_DETAILS") ScaleformMovieMethodAddParamPlayerNameString(("%s %s"):format(NDCore.player.firstname, NDCore.player.lastname)) - PushScaleformMovieFunctionParameterString(("Cash: $%d"):format(NDCore.player.cash)) - PushScaleformMovieFunctionParameterString(("Bank: $%d"):format(NDCore.player.bank)) + ScaleformMovieMethodAddParamTextureNameString(("Cash: $%d"):format(NDCore.player.cash)) + ScaleformMovieMethodAddParamTextureNameString(("Bank: $%d"):format(NDCore.player.bank)) EndScaleformMovieMethod() elseif sleep == 0 then sleep = 500 From 426be404f32d9e91a3801710a594259518845205 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:31:20 +0200 Subject: [PATCH 002/161] update: query.sql --- query.sql | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/query.sql b/query.sql index 3f2b7ac..05e16f8 100644 --- a/query.sql +++ b/query.sql @@ -1,16 +1,15 @@ -CREATE TABLE `characters` ( - `character_id` INT(10) NOT NULL AUTO_INCREMENT, +CREATE TABLE IF NOT EXISTS `nd_characters` ( + `charid` INT(10) NOT NULL AUTO_INCREMENT, `license` VARCHAR(200) NOT NULL DEFAULT '0', - `first_name` VARCHAR(50) DEFAULT NULL, - `last_name` VARCHAR(50) DEFAULT NULL, + `name` VARCHAR(50) DEFAULT NULL, + `firstname` VARCHAR(50) DEFAULT NULL, + `lastname` VARCHAR(50) DEFAULT NULL, `dob` VARCHAR(50) DEFAULT NULL, `gender` VARCHAR(50) DEFAULT NULL, `cash` INT(10) DEFAULT '0', `bank` INT(10) DEFAULT '0', - `job` VARCHAR(50) DEFAULT NULL, - `phone_number` VARCHAR(20) DEFAULT NULL, - `data` LONGTEXT DEFAULT '[]', + `groups` LONGTEXT DEFAULT '[]', + `metadata` LONGTEXT DEFAULT '[]', `inventory` LONGTEXT DEFAULT '[]', - `last_location` LONGTEXT DEFAULT '[]', - PRIMARY KEY (`character_id`) USING BTREE -); \ No newline at end of file + PRIMARY KEY (`charid`) USING BTREE +); From 72f4b011ece25848dcb697bfbf4d25a63df80ac3 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:31:42 +0200 Subject: [PATCH 003/161] feat: load query.sql --- server/main.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/main.lua b/server/main.lua index a06c33b..18aa20b 100644 --- a/server/main.lua +++ b/server/main.lua @@ -9,3 +9,8 @@ AddEventHandler("playerDropped", function() end) SetConvarServerInfo("ND_Core", GetResourceMetadata(GetCurrentResourceName(), "version", 0) or "invalid") + +local file = LoadResourceFile(GetCurrentResourceName(), "query.sql") +if file then + MySQL.query(file) +end From a4b40272bdc48c2f0fff12de1deacce030f1e905 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:31:59 +0200 Subject: [PATCH 004/161] tweak: starting files --- fxmanifest.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index 9ec131d..94c075d 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -16,8 +16,8 @@ client_scripts { server_scripts { "@oxmysql/lib/MySQL.lua", "server/main.lua", - "server/functions.lua", - "server/player.lua" + "server/player.lua", + "server/functions.lua" } shared_scripts { "init.lua" From 15a65712bbc65029e143ac6f685d991ce9f777aa Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:45:32 +0200 Subject: [PATCH 005/161] Update player.lua --- server/player.lua | 135 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 33 deletions(-) diff --git a/server/player.lua b/server/player.lua index 6efda90..77e858a 100644 --- a/server/player.lua +++ b/server/player.lua @@ -1,6 +1,7 @@ local function createCharacterTable(info) local self = { source = info.source, + license = info.license, name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -8,73 +9,94 @@ local function createCharacterTable(info) gender = info.gender, cash = info.cash, bank = info.bank, - license = info.license, + groups = info.groups, metadata = info.metadata, inventory = info.inventory } - - function self.delete() - local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id}) - if result and self.source then - ActivePlayers[self.source] = nil - end - return result - end + ---@param account string + ---@param amount number + ---@param reason string|nil + ---@return boolean function self.deductMoney(account, amount, reason) local amount = tonumber(amount) if not amount or account ~= "bank" and account ~= "cash" then return end self[account] -= amount if self.source then TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason) - ActivePlayers[self.source] = self end return true end + ---@param account string + ---@param amount number + ---@param reason string|nil + ---@return boolean function self.addMoney(account, amount, reason) local amount = tonumber(amount) if not amount or account ~= "bank" and account ~= "cash" then return end self[account] += amount if self.source then TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason) - ActivePlayers[self.source] = self end return true end - + + ---@param amount number + ---@return boolean function self.depositMoney(amount) local amount = tonumber(amount) if not amount or self.cash < amount or amount <= 0 then return end - return self:deductMoney("cash", amount, "Deposit") and self:AddMoney("bank", amount, "Deposit") + return self.deductMoney("cash", amount, "Deposit") and self.addMoney("bank", amount, "Deposit") end + ---@param amount number + ---@return boolean function self.withdrawMoney(amount) local amount = tonumber(amount) if not amount or self.bank < amount or amount <= 0 then return end - return self:deductMoney("bank", amount, "Withdraw") and self:AddMoney("cash", amount, "Withdraw") + return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw") end + ---@param key string + ---@param value any + ---@return table function self.setMetadata(key, value) self.metadata[key] = value - if self.source then - ActivePlayers[self.source] = self - return ActivePlayers[self.source].metadata - end return self.metadata end + + -- Completely delete character + function self.delete() + local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id}) + if result and ActivePlayers[self.source] then + ActivePlayers[self.source] = nil + end + return result + end - function self.unload(self) - TriggerEvent("ND:characterUnloaded", self.source, self) - local ped = GetPlayerPed(self.source) - self:setMetadata("location", GetEntityCoords(ped)) - self:save() + -- Unload and save character + function self.unload() if not self.source then return end + local ped = GetPlayerPed(self.source) + if ped then + local coords = GetEntityCoords(ped) + local heading = GetEntityHeading(ped) + self.setMetadata("location", { + x = coords.x, + y = coords.y, + x = coords.z, + w = heading + }) + end + TriggerEvent("ND:characterUnloaded", self.source, self) + self.save() ActivePlayers[self.source] = nil end - function self.save(self) - MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, metadata = ?, inventory = ? WHERE charid = ?", { + -- Save character information to database + function self.save() + MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ?, inventory = ? WHERE charid = ?", { self.name, self.firstname, self.lastname, @@ -82,12 +104,16 @@ local function createCharacterTable(info) self.gender, self.cash, self.bank, + json.encode(self.groups), json.encode(self.metadata), json.encode(self.inventory), self.id, }) end + ---Create a license/permit for the character + ---@param licenseType string + ---@param expire number function self.createLicense(licenseType, expire) local expireIn = tonumber(expire) or 2592000 local time = os.time() @@ -111,19 +137,59 @@ local function createCharacterTable(info) return end self.metadata.licenses = {license} - - if not self.source then return end - ActivePlayers[self.source] = self end + ---@param coords vector3|vector4 + ---@return boolean function self.setCoords(coords) if not self.source or not coords then return end local ped = GetPlayerPed(self.source) if not DoesEntityExist(ped) then return end SetEntityCoords(ped, coords.x, coords.y, coords.z) + if coords.w then + SetEntityHeading(ped, coords.w) + end return true end + ---@param eventName string + ---@param ... any + ---@return boolean + function self.triggerEvent(eventName, ...) + if not self.source then return end + TriggerClientEvent(eventName, self.source, ...) + return true + end + + ---@param reason string + function self.drop(reason) + if not self.source then return end + DropPlayer(self.source, reason) + end + + -- Set the character as the players active character/currently playing character + function self.active() + local char = ActivePlayers[self.source] + if char and char.id == self.id then return true end + if char then char:unload() end + ActivePlayers[self.source] = self + TriggerEvent("ND:characterLoaded", self) + TriggerClientEvent("ND:characterLoaded", self.source, self) + end + + ---@param name string + ---@param rank number + function self.addGroup(name, rank) + self.groups[name] = { + + } + end + + ---@param name string + function self.removeGroup(name) + self.groups[name] = nil + end + return self end @@ -133,6 +199,7 @@ function NDCore.newCharacter(src, info) local charInfo = createCharacterTable({ source = src, + license = license, name = GetPlayerName(src) or "", firstname = info.firstname or "", lastname = info.lastname or "", @@ -140,7 +207,7 @@ function NDCore.newCharacter(src, info) gender = info.gender or "", cash = info.cash or 0, bank = info.bank or 0, - license = license, + groups = info.groups or {}, metadata = info.metadata or {}, inventory = info.inventory or {}, }) @@ -154,7 +221,7 @@ function NDCore.newCharacter(src, info) charInfo.gender, charInfo.cash, charInfo.bank, - charInfo.metadata + json.encode(charInfo.metadata) }) return charInfo @@ -167,6 +234,7 @@ function NDCore.fetchCharacter(id) local info = result[1] return createCharacterTable({ id = id, + license = info.license, name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -174,9 +242,9 @@ function NDCore.fetchCharacter(id) gender = info.gender, cash = info.cash, bank = info.bank, - license = info.license, + groups = json.decode(info.groups), metadata = json.decode(info.metadata), - inventory = json.decode(i.inventory) + inventory = json.decode(info.inventory) }) end @@ -189,6 +257,7 @@ function NDCore.getPlayerCharacters(src) local info = result[i] characters[info.charid] = createCharacterTable({ id = info.charid, + license = info.license, name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -196,7 +265,7 @@ function NDCore.getPlayerCharacters(src) gender = info.gender, cash = info.cash, bank = info.bank, - license = info.license, + groups = json.decode(info.groups), metadata = json.decode(info.metadata), inventory = json.decode(info.inventory), }) From fb933c4e5830c633d2322f951f2d1465b5f8247f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 03:17:32 +0200 Subject: [PATCH 006/161] update: gorups --- init.lua | 1 + server/functions.lua | 5 +++++ server/player.lua | 24 +++++++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index b0540a3..7f25d1e 100644 --- a/init.lua +++ b/init.lua @@ -8,6 +8,7 @@ Config = { discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), discordActionText2 = GetConvar("core:discordActionText2", "STORE"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), + groups = json.decode(GetConvar("core:groups")) or {} } local nd_core = exports["ND_Core"] diff --git a/server/functions.lua b/server/functions.lua index 8d09f33..fd084e8 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -1,7 +1,12 @@ +---@param src number +---@return table function NDCore.getPlayer(src) return ActivePlayers[src] end +---@param metadata string +---@param data any +---@return table function NDCore.getPlayers(metadata, data) if not metadata or not data then return ActivePlayers end local players = {} diff --git a/server/player.lua b/server/player.lua index 77e858a..5f21df5 100644 --- a/server/player.lua +++ b/server/player.lua @@ -179,10 +179,22 @@ local function createCharacterTable(info) ---@param name string ---@param rank number + ---@return boolean function self.addGroup(name, rank) + local groupInfo = Config.groups[name] + if not groupInfo return end self.groups[name] = { - + label = groupInfo.label, + rankName = groupInfo.ranks[rank] or rank, + rank = rank } + return true + end + + ---@param name string + ---@return table + function self.getGroup(name) + return self.groups[name] end ---@param name string @@ -193,6 +205,9 @@ local function createCharacterTable(info) return self end +---@param src number +---@param info table +---@return table function NDCore.newCharacter(src, info) local license = GetPlayerIdentifierByType(src, "license") if not license then return end @@ -227,6 +242,8 @@ function NDCore.newCharacter(src, info) return charInfo end +---@param id number +---@return table function NDCore.fetchCharacter(id) local result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id}) if not result then return end @@ -248,6 +265,8 @@ function NDCore.fetchCharacter(id) }) end +---@param src number +---@return table function NDCore.getPlayerCharacters(src) local characters = {} local result = MySQL.query.await("SELECT * FROM nd_characters WHERE license = ?", {GetPlayerIdentifierByType(src, "license")}) @@ -273,6 +292,9 @@ function NDCore.getPlayerCharacters(src) return characters end +---@param src number +---@param id number +---@return table function NDCore.setActiveCharacter(src, id) local char = ActivePlayers[src] if char then char:unload() end From 177f547f8024145f5af931ef1119ac7f944587bf Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 03:33:01 +0200 Subject: [PATCH 007/161] feat: change license to any identifier --- init.lua | 3 ++- server/player.lua | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/init.lua b/init.lua index 7f25d1e..c84bbf1 100644 --- a/init.lua +++ b/init.lua @@ -8,7 +8,8 @@ Config = { discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), discordActionText2 = GetConvar("core:discordActionText2", "STORE"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), - groups = json.decode(GetConvar("core:groups")) or {} + groups = json.decode(GetConvar("core:groups")) or {}, + characterIdentifier = GetConvar("core:characterIdentifier", "license") } local nd_core = exports["ND_Core"] diff --git a/server/player.lua b/server/player.lua index 5f21df5..c459f9e 100644 --- a/server/player.lua +++ b/server/player.lua @@ -1,7 +1,7 @@ local function createCharacterTable(info) local self = { source = info.source, - license = info.license, + identifier = info.identifier, name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -209,12 +209,12 @@ end ---@param info table ---@return table function NDCore.newCharacter(src, info) - local license = GetPlayerIdentifierByType(src, "license") - if not license then return end + local identifier = GetPlayerIdentifierByType(src, Config.characterIdentifier) + if not identifier then return end local charInfo = createCharacterTable({ source = src, - license = license, + identifier = identifier, name = GetPlayerName(src) or "", firstname = info.firstname or "", lastname = info.lastname or "", @@ -227,8 +227,8 @@ function NDCore.newCharacter(src, info) inventory = info.inventory or {}, }) - charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (license, name, firstname, lastname, dob, gender, cash, bank, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", { - license, + charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", { + identifier, charInfo.name, charInfo.firstname, charInfo.lastname, @@ -251,7 +251,7 @@ function NDCore.fetchCharacter(id) local info = result[1] return createCharacterTable({ id = id, - license = info.license, + identifier = info.identifier, name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -269,14 +269,14 @@ end ---@return table function NDCore.getPlayerCharacters(src) local characters = {} - local result = MySQL.query.await("SELECT * FROM nd_characters WHERE license = ?", {GetPlayerIdentifierByType(src, "license")}) + local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {GetPlayerIdentifierByType(src, Config.characterIdentifier)}) local amount = #result for i=1, amount do local info = result[i] characters[info.charid] = createCharacterTable({ id = info.charid, - license = info.license, + identifier = info.identifier, name = info.name, firstname = info.firstname, lastname = info.lastname, From e2c6f6b97d429aed15d0324556b88b9c633ea2dd Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 03:40:54 +0200 Subject: [PATCH 008/161] Update init.lua --- init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index c84bbf1..b88768f 100644 --- a/init.lua +++ b/init.lua @@ -9,7 +9,9 @@ Config = { discordActionText2 = GetConvar("core:discordActionText2", "STORE"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), groups = json.decode(GetConvar("core:groups")) or {}, - characterIdentifier = GetConvar("core:characterIdentifier", "license") + characterIdentifier = GetConvar("core:characterIdentifier", "license"), + randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), + disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1 } local nd_core = exports["ND_Core"] From 664f48acd601ff9fdcb589a9b04e201c8a92aef5 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 11 Jul 2023 00:35:11 +0200 Subject: [PATCH 009/161] Update player.lua --- server/player.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/player.lua b/server/player.lua index c459f9e..bd78d0b 100644 --- a/server/player.lua +++ b/server/player.lua @@ -1,7 +1,11 @@ local function createCharacterTable(info) + local playerInfo = PlayersInfo[info.source] or {} + local self = { source = info.source, identifier = info.identifier, + identifiers = playerInfo.identifiers or {}, + discord = playerInfo.discord or {} name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -182,7 +186,7 @@ local function createCharacterTable(info) ---@return boolean function self.addGroup(name, rank) local groupInfo = Config.groups[name] - if not groupInfo return end + if not groupInfo then return end self.groups[name] = { label = groupInfo.label, rankName = groupInfo.ranks[rank] or rank, @@ -267,12 +271,11 @@ end ---@param src number ---@return table -function NDCore.getPlayerCharacters(src) +function NDCore.fetchAllCharacters(src) local characters = {} local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {GetPlayerIdentifierByType(src, Config.characterIdentifier)}) - local amount = #result - for i=1, amount do + for i=1, #result do local info = result[i] characters[info.charid] = createCharacterTable({ id = info.charid, From d8cb79adf50fce92ebd7f946a42740fe9f4eacc7 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 11 Jul 2023 01:05:13 +0200 Subject: [PATCH 010/161] feat: discord stuff --- init.lua | 4 ++- server/main.lua | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index b88768f..6ad0633 100644 --- a/init.lua +++ b/init.lua @@ -11,7 +11,9 @@ Config = { groups = json.decode(GetConvar("core:groups")) or {}, characterIdentifier = GetConvar("core:characterIdentifier", "license"), randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), - disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1 + disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, + discordGuildId = GetConvar("core:discordGuildId"), + discordBotToken = GetConvar("core:discordBotToken") } local nd_core = exports["ND_Core"] diff --git a/server/main.lua b/server/main.lua index 18aa20b..629634a 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,5 +1,7 @@ NDCore = {} ActivePlayers = {} +PlayersInfo = {} +local tempPlayersInfo = {} AddEventHandler("playerDropped", function() local src = source @@ -9,8 +11,91 @@ AddEventHandler("playerDropped", function() end) SetConvarServerInfo("ND_Core", GetResourceMetadata(GetCurrentResourceName(), "version", 0) or "invalid") +SetConvarReplicated("inventory:framework", "nd") local file = LoadResourceFile(GetCurrentResourceName(), "query.sql") if file then MySQL.query(file) end + +local function getIdentifierList(src) + local list = {} + for i=0, GetNumPlayerIdentifiers(src) do + local identifier = GetPlayerIdentifier(src, i) + if identifier then + local colon = identifier:find(":") + list[identifierType] = identifier:sub(1, colon-1) + end + end + return list +end + +local discordErrors = { + [400] = "Improper HTTP request", + [401] = "Discord bot token might be missing or incorrect", + [404] = "User might not be in the server", + [429] = "Discord bot rate limited" +} + +local function getDiscordInfo(discordUserId) + local done = false + local data + + PerformHttpRequest(("https://discordapp.com/api/guilds/%s/members/%s"):format(Config.discordGuildId, discordUserId), function(errorCode, resultData, resultHeaders) + if errorCode ~= 200 then + done = true + return print(("Error %d: %s"):format(errorCode, discordErrors[errorCode])) + end + + local result = json.decode(resultData) + data = { + nickname = result.nick or result.user.username, + user = result.user, + roles = result.roles + } + done = true + end, "GET", "", {["Content-Type"] = "application/json", ["Authorization"] = ("Bot %s"):format(Config.discordBotToken)}) + + while not done do Wait(500) end + return data +end + +AddEventHandler("playerJoining", function(oldId) + local src = source + PlayersInfo[src] = tempPlayersInfo[oldId] + tempPlayersInfo[oldId] = nil +end) + +AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) + local tempSrc = source + local identifiers = getIdentifierList(tempSrc) + local mainIdentifier = identifiers[Config.characterIdentifier] + local discordInfo = {} + + deferrals.defer() + Wait(0) + + if mainIdentifier and Config.discordBotToken and Config.discordGuildId then + local discordIdentifier = identifiers["discord"] + if not discordIdentifier then + deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite)) + Wait(0) + return + end + discordInfo = getDiscordInfo(discordIdentifier:gsub("discord:")) + end + + deferrals.update("Connecting...") + Wait(0) + + if mainIdentifier then + tempPlayersInfo[tempSrc] = { + identifiers = identifiers, + discord = discordInfo + } + deferrals.done() + else + deferrals.done(("Your %s was not found."):format(Config.characterIdentifier)) + Wait(0) + end +end) From 2068e7bb0b34c87910fcbec9ff6dd436467d7b93 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 11 Jul 2023 01:06:00 +0200 Subject: [PATCH 011/161] feat: isResourceStarted function --- shared/functions.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 shared/functions.lua diff --git a/shared/functions.lua b/shared/functions.lua new file mode 100644 index 0000000..76a3910 --- /dev/null +++ b/shared/functions.lua @@ -0,0 +1,28 @@ +local startedResources = {} +local callbacks = {} + +AddEventHandler("onResourceStart", function(resourceName) + startedResources[resourceName] = true + local callback = callbacks[resourceName] + if not callback then return end + callback(true) +end) + +AddEventHandler("onResourceStop", function(resourceName) + startedResources[resourceName] = nil + local callback = callbacks[resourceName] + if not callback then return end + callback(false) +end) + +function NDCore.isResourceStarted(resourceName, cb) + local started = GetResourceState(resourceName) == "started" + startedResources[resourceName] = started + + if cb then + callbacks[resourceName] = cb + cb(started) + end + + return started +end From d5d3de98bf68e691878a3b7ca29597b7c0c0d996 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 11 Jul 2023 01:06:13 +0200 Subject: [PATCH 012/161] Update fxmanifest.lua --- fxmanifest.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index 94c075d..b58afdc 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -10,17 +10,17 @@ lua54 "yes" client_scripts { "client/main.lua", + "shared/functions.lua", "client/functions.lua", "client/events.lua", } server_scripts { "@oxmysql/lib/MySQL.lua", "server/main.lua", + "shared/functions.lua", "server/player.lua", "server/functions.lua" } -shared_scripts { - "init.lua" -} +shared_script "init.lua" dependency "oxmysql" From af7d89316451b175b2b8ee2b3a133a0068c7bc0d Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:51:04 +0200 Subject: [PATCH 013/161] refactor: database queries --- database/vehicles.sql | 12 ++++++++++++ query.sql | 15 --------------- server/main.lua | 10 ++++++++++ 3 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 database/vehicles.sql delete mode 100644 query.sql diff --git a/database/vehicles.sql b/database/vehicles.sql new file mode 100644 index 0000000..4448a8e --- /dev/null +++ b/database/vehicles.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS `nd_vehicles` ( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `owner` INT(11) DEFAULT NULL, + `plate` VARCHAR(255) DEFAULT NULL, + `glovebox` LONGTEXT DEFAULT '[]', + `trunk` LONGTEXT DEFAULT '[]', + `properties` LONGTEXT DEFAULT '[]', + `stored` INT(11) DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `owner` (`owner`) USING BTREE, + CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE +); diff --git a/query.sql b/query.sql deleted file mode 100644 index 05e16f8..0000000 --- a/query.sql +++ /dev/null @@ -1,15 +0,0 @@ -CREATE TABLE IF NOT EXISTS `nd_characters` ( - `charid` INT(10) NOT NULL AUTO_INCREMENT, - `license` VARCHAR(200) NOT NULL DEFAULT '0', - `name` VARCHAR(50) DEFAULT NULL, - `firstname` VARCHAR(50) DEFAULT NULL, - `lastname` VARCHAR(50) DEFAULT NULL, - `dob` VARCHAR(50) DEFAULT NULL, - `gender` VARCHAR(50) DEFAULT NULL, - `cash` INT(10) DEFAULT '0', - `bank` INT(10) DEFAULT '0', - `groups` LONGTEXT DEFAULT '[]', - `metadata` LONGTEXT DEFAULT '[]', - `inventory` LONGTEXT DEFAULT '[]', - PRIMARY KEY (`charid`) USING BTREE -); diff --git a/server/main.lua b/server/main.lua index 629634a..ab94421 100644 --- a/server/main.lua +++ b/server/main.lua @@ -2,6 +2,10 @@ NDCore = {} ActivePlayers = {} PlayersInfo = {} local tempPlayersInfo = {} +local databaseFiles = { + "database/characters.sql", + "database/vehicles.sql" +} AddEventHandler("playerDropped", function() local src = source @@ -99,3 +103,9 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) Wait(0) end end) +for i=1, #databaseFiles do + local file = LoadResourceFile(resourceName, databaseFiles[i]) + if file then + MySQL.query(file) + end +end From f1d1346f3f0bb71e3017d9174598b48029b8be85 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:51:50 +0200 Subject: [PATCH 014/161] fix: convar errors on client --- client/main.lua | 16 ++++++++++++++++ init.lua | 18 ------------------ server/main.lua | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/client/main.lua b/client/main.lua index 2d6ccf0..506de6a 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,5 +1,21 @@ NDCore = {} +Config = { + serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), + discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), + discordAppId = GetConvar("core:discordAppId", "858146067018416128"), + discordAsset = GetConvar("core:discordAsset", "andyyy"), + discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), + discordActionText = GetConvar("core:discordActionText", "DISCORD"), + discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), + discordActionText2 = GetConvar("core:discordActionText2", "STORE"), + discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), + randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), + disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, + useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, + groups = json.decode(GetConvar("core:groups", "[]")) +} + CreateThread(function() SetDiscordAppId(Config.discordAppId) SetDiscordRichPresenceAsset(Config.discordAsset) diff --git a/init.lua b/init.lua index 6ad0633..499944f 100644 --- a/init.lua +++ b/init.lua @@ -1,21 +1,3 @@ -Config = { - serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), - discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), - discordAppId = GetConvar("core:discordAppId", "858146067018416128"), - discordAsset = GetConvar("core:discordAsset", "andyyy"), - discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), - discordActionText = GetConvar("core:discordActionText", "DISCORD"), - discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), - discordActionText2 = GetConvar("core:discordActionText2", "STORE"), - discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), - groups = json.decode(GetConvar("core:groups")) or {}, - characterIdentifier = GetConvar("core:characterIdentifier", "license"), - randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), - disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, - discordGuildId = GetConvar("core:discordGuildId"), - discordBotToken = GetConvar("core:discordBotToken") -} - local nd_core = exports["ND_Core"] NDCore = setmetatable({}, { diff --git a/server/main.lua b/server/main.lua index ab94421..d108d2c 100644 --- a/server/main.lua +++ b/server/main.lua @@ -13,6 +13,24 @@ AddEventHandler("playerDropped", function() if not char then return end char:unload() end) +Config = { + serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), + discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), + discordAppId = GetConvar("core:discordAppId", "858146067018416128"), + discordAsset = GetConvar("core:discordAsset", "andyyy"), + discordAssetSmall = GetConvar("core:discordAssetSmall", "andyyy"), + discordActionText = GetConvar("core:discordActionText", "DISCORD"), + discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), + discordActionText2 = GetConvar("core:discordActionText2", "STORE"), + discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), + characterIdentifier = GetConvar("core:characterIdentifier", "license"), + discordGuildId = GetConvar("core:discordGuildId"), + discordBotToken = GetConvar("core:discordBotToken"), + randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), + disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, + useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, + groups = json.decode(GetConvar("core:groups", "[]")) +} SetConvarServerInfo("ND_Core", GetResourceMetadata(GetCurrentResourceName(), "version", 0) or "invalid") SetConvarReplicated("inventory:framework", "nd") From 47e7bc51510e76c5f7b50eeb262a569fef1bc699 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:52:21 +0200 Subject: [PATCH 015/161] refactor: database queries --- database/characters.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 database/characters.sql diff --git a/database/characters.sql b/database/characters.sql new file mode 100644 index 0000000..4853fa9 --- /dev/null +++ b/database/characters.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS `nd_characters` ( + `charid` INT(10) NOT NULL AUTO_INCREMENT, + `license` VARCHAR(200) NOT NULL DEFAULT '0', + `name` VARCHAR(50) DEFAULT NULL, + `firstname` VARCHAR(50) DEFAULT NULL, + `lastname` VARCHAR(50) DEFAULT NULL, + `dob` VARCHAR(50) DEFAULT NULL, + `gender` VARCHAR(50) DEFAULT NULL, + `cash` INT(10) DEFAULT '0', + `bank` INT(10) DEFAULT '0', + `groups` LONGTEXT DEFAULT '[]', + `metadata` LONGTEXT DEFAULT '[]', + `inventory` LONGTEXT DEFAULT '[]', + PRIMARY KEY (`charid`) USING BTREE +); From fd7a22423fc4ff2f89f2ac883154ebf92f3ef47e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:53:00 +0200 Subject: [PATCH 016/161] refactor(server/main): warnings and identifiers --- server/main.lua | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/server/main.lua b/server/main.lua index d108d2c..f2e85eb 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,18 +1,19 @@ NDCore = {} ActivePlayers = {} PlayersInfo = {} +local resourceName = GetCurrentResourceName() local tempPlayersInfo = {} local databaseFiles = { "database/characters.sql", "database/vehicles.sql" } +local discordErrors = { + [400] = "Improper HTTP request", + [401] = "Discord bot token might be missing or incorrect", + [404] = "User might not be in the server", + [429] = "Discord bot rate limited" +} -AddEventHandler("playerDropped", function() - local src = source - local char = ActivePlayers[src] - if not char then return end - char:unload() -end) Config = { serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), discordInvite = GetConvar("core:discordInvite", "https://discord.gg/Z9Mxu72zZ6"), @@ -32,33 +33,22 @@ Config = { groups = json.decode(GetConvar("core:groups", "[]")) } -SetConvarServerInfo("ND_Core", GetResourceMetadata(GetCurrentResourceName(), "version", 0) or "invalid") +SetConvarServerInfo("ND_Core", GetResourceMetadata(resourceName, "version", 0) or "invalid") SetConvarReplicated("inventory:framework", "nd") -local file = LoadResourceFile(GetCurrentResourceName(), "query.sql") -if file then - MySQL.query(file) -end - local function getIdentifierList(src) local list = {} for i=0, GetNumPlayerIdentifiers(src) do local identifier = GetPlayerIdentifier(src, i) if identifier then local colon = identifier:find(":") - list[identifierType] = identifier:sub(1, colon-1) + local identifierType = identifier:sub(1, colon-1) + list[identifierType] = identifier end end return list end -local discordErrors = { - [400] = "Improper HTTP request", - [401] = "Discord bot token might be missing or incorrect", - [404] = "User might not be in the server", - [429] = "Discord bot rate limited" -} - local function getDiscordInfo(discordUserId) local done = false local data @@ -66,7 +56,7 @@ local function getDiscordInfo(discordUserId) PerformHttpRequest(("https://discordapp.com/api/guilds/%s/members/%s"):format(Config.discordGuildId, discordUserId), function(errorCode, resultData, resultHeaders) if errorCode ~= 200 then done = true - return print(("Error %d: %s"):format(errorCode, discordErrors[errorCode])) + return print(("^3Warning: %d %s"):format(errorCode, discordErrors[errorCode])) end local result = json.decode(resultData) @@ -104,7 +94,7 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) Wait(0) return end - discordInfo = getDiscordInfo(discordIdentifier:gsub("discord:")) + discordInfo = getDiscordInfo(discordIdentifier:gsub("discord:", "")) end deferrals.update("Connecting...") @@ -121,6 +111,16 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) Wait(0) end end) + +AddEventHandler("playerDropped", function() + local src = source + local char = ActivePlayers[src] + if char then + char:unload() + end + PlayersInfo[src] = nil +end) + for i=1, #databaseFiles do local file = LoadResourceFile(resourceName, databaseFiles[i]) if file then From c3d2d8ad73e1f96fbccaec02eba3ffb1517ddabf Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:53:14 +0200 Subject: [PATCH 017/161] Update fxmanifest.lua --- fxmanifest.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index b58afdc..7632cee 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -11,14 +11,16 @@ lua54 "yes" client_scripts { "client/main.lua", "shared/functions.lua", + "client/vehicle.lua", "client/functions.lua", - "client/events.lua", + "client/events.lua" } server_scripts { "@oxmysql/lib/MySQL.lua", "server/main.lua", "shared/functions.lua", "server/player.lua", + "server/vehicle.lua", "server/functions.lua" } shared_script "init.lua" From 5674e424a85cc222dabcc291724427799f94e5da Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:53:18 +0200 Subject: [PATCH 018/161] Update functions.lua --- shared/functions.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shared/functions.lua b/shared/functions.lua index 76a3910..4bc42da 100644 --- a/shared/functions.lua +++ b/shared/functions.lua @@ -17,12 +17,10 @@ end) function NDCore.isResourceStarted(resourceName, cb) local started = GetResourceState(resourceName) == "started" - startedResources[resourceName] = started - if cb then + startedResources[resourceName] = started callbacks[resourceName] = cb cb(started) end - return started end From 9da69aa3506b42eb4f0882fbbb4fbdfab3999287 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:53:35 +0200 Subject: [PATCH 019/161] feat: notify player function --- server/player.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/player.lua b/server/player.lua index bd78d0b..ec34dee 100644 --- a/server/player.lua +++ b/server/player.lua @@ -5,7 +5,7 @@ local function createCharacterTable(info) source = info.source, identifier = info.identifier, identifiers = playerInfo.identifiers or {}, - discord = playerInfo.discord or {} + discord = playerInfo.discord or {}, name = info.name, firstname = info.firstname, lastname = info.lastname, @@ -165,6 +165,13 @@ local function createCharacterTable(info) return true end + + function self.notify(...) + if not self.source then return end + TriggerClientEvent("ox_lib:notify", self.source, ...) + return true + end + ---@param reason string function self.drop(reason) if not self.source then return end From f82976a822bc9fcd79c6f6013385025f6df7fd4e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:54:03 +0200 Subject: [PATCH 020/161] wip: vehicles --- server/vehicle.lua | 290 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 server/vehicle.lua diff --git a/server/vehicle.lua b/server/vehicle.lua new file mode 100644 index 0000000..ad8351f --- /dev/null +++ b/server/vehicle.lua @@ -0,0 +1,290 @@ +local playerOwnedVehicles = {} + +local function isPlateAvailable(plate) + return not MySQL.scalar.await("SELECT 1 FROM vehicles WHERE plate = ?", {plate}) +end + +local function generatePlate() + local plate = {} + for i = 1, 8 do + plate[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) + end + return table.concat(plate) +end + +local function generatePlateWait() + local plate = generatePlate() + while not isPlateAvailable(plate) do + plate = generatePlate() + end + return plate +end + +local function getVehicles(characterId) + local result = MySQL.query.await("SELECT * FROM vehicles WHERE owner = ?", {characterId}) + if not result then return {} end + local vehicles = {} + for _, vehicle in pairs(result) do + local key = #vehicles + 1 + vehicles[key] = {} + vehicles[key].available = vehicle.stored == 1 + vehicles[key].owner = vehicle.owner + vehicles[key].id = vehicle.id + vehicles[key].plate = vehicle.plate + vehicles[key].properties = json.decode(vehicle.properties) + end + return vehicles +end + +function NDCore.transferVehicle(vehicleID, fromSource, toSource) + local playerTo = NDCore.getPlayer(toSource) + local playerFrom = NDCore.getPlayer(fromSource) + MySQL.query.await("UPDATE nd_vehicles SET owner = ? WHERE id = ?", {playerTo.id, vehicleID}) + + if not playerOwnedVehicles[vehicleID] then return end + local veh = NetworkGetEntityFromNetworkId(playerOwnedVehicles[vehicleID].netid) + if not veh then + playerFrom:notify({ + title = "Ownership transfered", + description = "Vehicle ownership of has been transfered.", + type = "success", + position = "bottom-right", + duration = 4000 + }) + playerTo:notify({ + title = "Ownership received", + description = "Received vehicle ownership.", + type = "inform", + position = "bottom-right", + duration = 4000 + }) + return + end + + local state = Entity(veh).state + state.owner = playerTo.id + state.keys = { + [playerTo.id] = true + } + + playerFrom:triggerEvent("ND_Vehicles:blip", playerOwnedVehicles[vehicleID].netid, false) + + playerFrom:notify({ + title = "Ownership transfered", + description = ("Vehicle ownership of %s has been transfered."):format(GetVehicleNumberPlateText(veh)), + type = "success", + position = "bottom-right", + duration = 4000 + }) + playerTo:notify({ + title = "Ownership received", + description = ("Received vehicle ownership of %s."):format(GetVehicleNumberPlateText(veh)), + type = "inform", + position = "bottom-right", + duration = 4000 + }) +end + +function NDCore.setVehicleOwned(src, properties, stored) + local player = NDCore.getPlayer(src) + local plate = generatePlateWait() + properties.plate = plate + local id = MySQL.insert.await("INSERT INTO vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {player.id, properties.plate, json.encode(properties), stored and 1 or 0}) + local vehicles = getVehicles(player.id) + player:triggerEvent("ND_Vehicles:returnVehicles", vehicles) + return id +end + +function NDCore.giveVehicleKeys(vehicle, source, target) + local state = Entity(vehicle).state + if not state then return end + + local player = NDCore.getPlayer(source) + local owner = state.owner + if not owner and owner ~= player.id then return end + + local keys = state.keys + if not keys then return end + + local targetPlayer = NDCore.getPlayer(target) + state.keys[targetPlayer.id] = true + + player:notify({ + title = "Keys shared", + description = ("You've shared vehicle keys to %s."):format(GetVehicleNumberPlateText(vehicle)), + type = "success", + position = "bottom-right", + duration = 4000 + }) + targetPlayer:notify({ + title = "Keys received", + description = ("Received vehicle keys to %s."):format(GetVehicleNumberPlateText(vehicle)), + type = "inform", + position = "bottom-right", + duration = 4000 + }) + return true +end + +function NDCore.giveVehicleAccess(source, vehicle) + local state = Entity(vehicle).state + if not state then return end + + local player = NDCore.getPlayer(source) + if not player then return end + + if not state.keys then + state.keys = { + [player.id] = true + } + else + local keys = state.keys + keys[player.id] = true + state.keys = keys + end + + player:triggerEvent("ND_Vehicles:setOwnedIfNot", NetworkGetNetworkIdFromEntity(vehicle)) +end + +local function isParkingAvailable(source, coords) + local tries = 1 + while tries < #coords do + Wait(300) + local coord = coords[math.random(1, #coords)] + local available = lib.callback.await("ND_Vehicles:getParkedVehicle", source, vector3(coord.x, coord.y, coord.z)) + if available then + return coord + end + tries += 1 + end + return false +end + +local function getVehicleType(model) + local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, false, false) + while not DoesEntityExist(tempVehicle) do Wait(0) end + local entityType = GetVehicleType(tempVehicle) + DeleteEntity(tempVehicle) + return entityType +end + +function NDCore.spawnOwnedVehicle(source, vehicleID, coords) + local player = NDCore.getPlayer(source) + local spawnCoords = coords + + if type(coords) == "table" then + spawnCoords = isParkingAvailable(source, coords) + if not spawnCoords then + player:notify({ + title = "Can't bring out vehicle", + description = "No parking spot available for your vehicle. It's still in your garage.", + type = "error", + position = "bottom", + duration = 4000 + }) + return + end + end + + local vehicles = getVehicles(player.id) + for _, vehicle in pairs(vehicles) do + if vehicle.owner == player.id and vehicle.id == vehicleID then + MySQL.query.await("UPDATE vehicles SET stored = ? WHERE id = ?", {0, vehicleID}) + player:triggerEvent("ND_VehicleSystem:returnVehicles", getVehicles(player.id)) + + local veh = CreateVehicleServerSetter(vehicle.properties.model, getVehicleType(vehicle.properties.model), spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w) + while not DoesEntityExist(veh) do Wait(0) end + + playerOwnedVehicles[vehicle.id] = { + netid = NetworkGetNetworkIdFromEntity(veh) + } + + local state = Entity(veh).state + state.owner = vehicle.owner + state.id = vehicle.id + state.props = vehicle.properties + state.keys = { + [player.id] = true + } + + return true + end + end +end + +function NDCore.returnVehicleToGarage(source, veh, properties) + local player = NDCore.getPlayer(source) + if not DoesEntityExist(veh) then return end + + local vehID = Entity(veh).state.id + local vehicles = getVehicles(player.id) + + for _, vehicle in pairs(vehicles) do + if vehicle.owner == player.id and vehicle.id == vehID then + MySQL.query.await("UPDATE vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(properties), 1, vehID}) + player:triggerEvent("ND_Vehicles:returnVehicles", getVehicles(player.id)) + DeleteEntity(veh) + return true + end + end +end + +function NDCore.saveVehicleProperties(source, veh, properties) + local player = NDCore.getPlayer(source) + if not DoesEntityExist(veh) then return end + + local vehID = Entity(veh).state.id + local vehicles = getVehicles(player.id) + + for _, vehicle in pairs(vehicles) do + if vehicle.owner == player.id and vehicle.id == vehID then + MySQL.query.await("UPDATE vehicles SET properties = ? WHERE id = ?", {json.encode(properties), vehID}) + return true + end + end +end + +RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid, success, action) + local veh = NetworkGetEntityFromNetworkId(netid) + local owner = NetworkGetEntityOwner(veh) + TriggerClientEvent("ND_Vehicles:syncAlarm", owner, netid, success, action) +end) + +if Config.useInventoryForKeys then + RegisterCommand("getkeys", function(source, args, rawCommand) + local veh = GetVehiclePedIsIn(GetPlayerPed(source)) + if not veh or veh == 0 then return end + + local player = NDCore.getPlayer(source) + local state = Entity(veh).state + local owner = state.owner + if not owner or owner ~= player.id then return end + + local props = state.props + local keys = state.keys + keys[player.id] = true + state.keys = keys + + exports.ox_inventory:AddItem(source, "keys", 1, { + vehOwner = owner, + vehId = state.id, + vehPlate = props.plate, + vehModel = props.model + }) + end, false) +else + RegisterCommand("givekeys", function(source, args, rawCommand) + local src = source + if not args[1] then return end + local target = tonumber(args[1]) + if not GetPlayerPing(target) then return end + + local veh = GetVehiclePedIsIn(GetPlayerPed(src)) + if veh == 0 then + veh = GetVehiclePedIsIn(GetPlayerPed(src), true) + if veh == 0 then return end + end + + giveKeys(veh, src, target) + end, false) +end From 310184cec3c4e5038ce489984a0c40dca2438168 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 12 Jul 2023 04:54:58 +0200 Subject: [PATCH 021/161] Delete .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index bafd2a7..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ - -config_server.lua From 1ab7765cff71a864eeaf637a2bc86c1f3af5ce2a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 13 Jul 2023 01:22:24 +0200 Subject: [PATCH 022/161] update: events and checks --- client/events.lua | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/client/events.lua b/client/events.lua index 8ae1397..bf60e7d 100644 --- a/client/events.lua +++ b/client/events.lua @@ -1,21 +1,30 @@ -RegisterNetEvent("ND:returnCharacters", function(characters) - NDCore.Characters = characters -end) - -- updates the money on the client. RegisterNetEvent("ND:updateMoney", function(cash, bank) + if not NDCore.player then return end NDCore.player.cash = cash NDCore.player.bank = bank end) +local function removeCharacterFunctions(character) + local newData = {} + for k, v in pairs(character) do + if type(v) ~= "function" then + newData[k] = v + end + end + return newData +end + -- Sets main character. -RegisterNetEvent("ND:setCharacter", function(character) - NDCore.player = character +RegisterNetEvent("ND:characterLoaded", function(character) + if not NDCore.player then return end + NDCore.player = removeCharacterFunctions(character) end) -- Update main character info. RegisterNetEvent("ND:updateCharacter", function(character) - NDCore.player = character + if not NDCore.player then return end + NDCore.player = removeCharacterFunctions(character) end) -- Updates last lcoation. @@ -23,10 +32,3 @@ RegisterNetEvent("ND:updateLastLocation", function(location) if not NDCore.player then return end NDCore.player.lastLocation = location end) - --- Enable pvp for players. -AddEventHandler("playerSpawned", function() - print("^0ND Framework support discord: ^5https://discord.gg/Z9Mxu72zZ6") - SetCanAttackFriendly(PlayerPedId(), true, false) - NetworkSetFriendlyFireOption(true) -end) From e2fb381eb90ce80f63bf5d7dc42b76514f50aa58 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 13 Jul 2023 01:22:34 +0200 Subject: [PATCH 023/161] Update main.lua --- client/main.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/client/main.lua b/client/main.lua index 506de6a..fbe254a 100644 --- a/client/main.lua +++ b/client/main.lua @@ -16,6 +16,7 @@ Config = { groups = json.decode(GetConvar("core:groups", "[]")) } +-- Discord rich presence. CreateThread(function() SetDiscordAppId(Config.discordAppId) SetDiscordRichPresenceAsset(Config.discordAsset) @@ -35,6 +36,7 @@ CreateThread(function() end end) +-- Pause menu information. CreateThread(function() AddTextEntry("FE_THDR_GTAO", Config.serverName) local sleep = 500 @@ -52,3 +54,15 @@ CreateThread(function() end end end) + +AddEventHandler("playerSpawned", function() + print("^0ND Framework support discord: ^5https://discord.gg/Z9Mxu72zZ6") + SetCanAttackFriendly(PlayerPedId(), true, false) + NetworkSetFriendlyFireOption(true) +end) + +AddEventHandler("onResourceStart", function(resourceName) + if resourceName ~= GetCurrentResourceName() then return end + SetCanAttackFriendly(PlayerPedId(), true, false) + NetworkSetFriendlyFireOption(true) +end) From 6f4ddba97659436443afa78b4e545a4cc7a4fdf1 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 13 Jul 2023 01:22:58 +0200 Subject: [PATCH 024/161] update(server/functions): findby more data --- server/functions.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/functions.lua b/server/functions.lua index fd084e8..c136ce7 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -7,11 +7,15 @@ end ---@param metadata string ---@param data any ---@return table -function NDCore.getPlayers(metadata, data) - if not metadata or not data then return ActivePlayers end +function NDCore.getPlayers(key, value) + if not key or not value then return ActivePlayers end + local players = {} + local keyTypes = {firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"} + local findBy = keyTypes[key] or "metadata" + for src, info in pairs(ActivePlayers) do - if info.metadata[metadata] == data then + if info[findBy][key] == value then players[src] = info end end From e19544c65e0d62006f9809162133ece8110e0c35 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 13 Jul 2023 01:23:23 +0200 Subject: [PATCH 025/161] update: colon and events --- server/main.lua | 2 +- server/player.lua | 21 ++++++++++++------- server/vehicle.lua | 52 ++++++++++++++++++++++++++-------------------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/server/main.lua b/server/main.lua index f2e85eb..4fc8470 100644 --- a/server/main.lua +++ b/server/main.lua @@ -116,7 +116,7 @@ AddEventHandler("playerDropped", function() local src = source local char = ActivePlayers[src] if char then - char:unload() + char.unload() end PlayersInfo[src] = nil end) diff --git a/server/player.lua b/server/player.lua index ec34dee..fb1ad2f 100644 --- a/server/player.lua +++ b/server/player.lua @@ -26,7 +26,8 @@ local function createCharacterTable(info) local amount = tonumber(amount) if not amount or account ~= "bank" and account ~= "cash" then return end self[account] -= amount - if self.source then + if ActivePlayers[self.source] then + self.triggerEvent("ND:updateMoney", self.cash, self.bank) TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason) end return true @@ -40,7 +41,8 @@ local function createCharacterTable(info) local amount = tonumber(amount) if not amount or account ~= "bank" and account ~= "cash" then return end self[account] += amount - if self.source then + if ActivePlayers[self.source] then + self.triggerEvent("ND:updateMoney", self.cash, self.bank) TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason) end return true @@ -67,6 +69,7 @@ local function createCharacterTable(info) ---@return table function self.setMetadata(key, value) self.metadata[key] = value + self.triggerEvent("ND:updateCharacter", self) return self.metadata end @@ -81,7 +84,7 @@ local function createCharacterTable(info) -- Unload and save character function self.unload() - if not self.source then return end + if not ActivePlayers[self.source] then return end local ped = GetPlayerPed(self.source) if ped then local coords = GetEntityCoords(ped) @@ -138,9 +141,10 @@ local function createCharacterTable(info) if licenses then self.metadata.licenses[#licenses+1] = license - return + else + self.metadata.licenses = {license} end - self.metadata.licenses = {license} + self.triggerEvent("ND:updateCharacter", self) end ---@param coords vector3|vector4 @@ -164,7 +168,6 @@ local function createCharacterTable(info) TriggerClientEvent(eventName, self.source, ...) return true end - function self.notify(...) if not self.source then return end @@ -182,7 +185,7 @@ local function createCharacterTable(info) function self.active() local char = ActivePlayers[self.source] if char and char.id == self.id then return true end - if char then char:unload() end + if char then char.unload() end ActivePlayers[self.source] = self TriggerEvent("ND:characterLoaded", self) TriggerClientEvent("ND:characterLoaded", self.source, self) @@ -199,6 +202,7 @@ local function createCharacterTable(info) rankName = groupInfo.ranks[rank] or rank, rank = rank } + self.triggerEvent("ND:updateCharacter", self) return true end @@ -211,6 +215,7 @@ local function createCharacterTable(info) ---@param name string function self.removeGroup(name) self.groups[name] = nil + self.triggerEvent("ND:updateCharacter", self) end return self @@ -307,7 +312,7 @@ end ---@return table function NDCore.setActiveCharacter(src, id) local char = ActivePlayers[src] - if char then char:unload() end + if char then char.unload() end local character = NDCore.fetchCharacter(id) character.source = src diff --git a/server/vehicle.lua b/server/vehicle.lua index ad8351f..d824cc5 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -44,14 +44,14 @@ function NDCore.transferVehicle(vehicleID, fromSource, toSource) if not playerOwnedVehicles[vehicleID] then return end local veh = NetworkGetEntityFromNetworkId(playerOwnedVehicles[vehicleID].netid) if not veh then - playerFrom:notify({ + playerFrom.notify({ title = "Ownership transfered", description = "Vehicle ownership of has been transfered.", type = "success", position = "bottom-right", duration = 4000 }) - playerTo:notify({ + playerTo.notify({ title = "Ownership received", description = "Received vehicle ownership.", type = "inform", @@ -67,16 +67,16 @@ function NDCore.transferVehicle(vehicleID, fromSource, toSource) [playerTo.id] = true } - playerFrom:triggerEvent("ND_Vehicles:blip", playerOwnedVehicles[vehicleID].netid, false) + playerFrom.triggerEvent("ND_Vehicles:blip", playerOwnedVehicles[vehicleID].netid, false) - playerFrom:notify({ + playerFrom.notify({ title = "Ownership transfered", description = ("Vehicle ownership of %s has been transfered."):format(GetVehicleNumberPlateText(veh)), type = "success", position = "bottom-right", duration = 4000 }) - playerTo:notify({ + playerTo.notify({ title = "Ownership received", description = ("Received vehicle ownership of %s."):format(GetVehicleNumberPlateText(veh)), type = "inform", @@ -91,7 +91,7 @@ function NDCore.setVehicleOwned(src, properties, stored) properties.plate = plate local id = MySQL.insert.await("INSERT INTO vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {player.id, properties.plate, json.encode(properties), stored and 1 or 0}) local vehicles = getVehicles(player.id) - player:triggerEvent("ND_Vehicles:returnVehicles", vehicles) + player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) return id end @@ -109,14 +109,14 @@ function NDCore.giveVehicleKeys(vehicle, source, target) local targetPlayer = NDCore.getPlayer(target) state.keys[targetPlayer.id] = true - player:notify({ + player.notify({ title = "Keys shared", description = ("You've shared vehicle keys to %s."):format(GetVehicleNumberPlateText(vehicle)), type = "success", position = "bottom-right", duration = 4000 }) - targetPlayer:notify({ + targetPlayer.notify({ title = "Keys received", description = ("Received vehicle keys to %s."):format(GetVehicleNumberPlateText(vehicle)), type = "inform", @@ -126,7 +126,7 @@ function NDCore.giveVehicleKeys(vehicle, source, target) return true end -function NDCore.giveVehicleAccess(source, vehicle) +function NDCore.giveVehicleAccess(source, vehicle, access) local state = Entity(vehicle).state if not state then return end @@ -135,15 +135,15 @@ function NDCore.giveVehicleAccess(source, vehicle) if not state.keys then state.keys = { - [player.id] = true + [player.id] = access } else local keys = state.keys - keys[player.id] = true + keys[player.id] = access state.keys = keys end - player:triggerEvent("ND_Vehicles:setOwnedIfNot", NetworkGetNetworkIdFromEntity(vehicle)) + player.triggerEvent("ND_Vehicles:setOwnedIfNot", NetworkGetNetworkIdFromEntity(vehicle)) end local function isParkingAvailable(source, coords) @@ -175,7 +175,7 @@ function NDCore.spawnOwnedVehicle(source, vehicleID, coords) if type(coords) == "table" then spawnCoords = isParkingAvailable(source, coords) if not spawnCoords then - player:notify({ + player.notify({ title = "Can't bring out vehicle", description = "No parking spot available for your vehicle. It's still in your garage.", type = "error", @@ -190,7 +190,7 @@ function NDCore.spawnOwnedVehicle(source, vehicleID, coords) for _, vehicle in pairs(vehicles) do if vehicle.owner == player.id and vehicle.id == vehicleID then MySQL.query.await("UPDATE vehicles SET stored = ? WHERE id = ?", {0, vehicleID}) - player:triggerEvent("ND_VehicleSystem:returnVehicles", getVehicles(player.id)) + player.triggerEvent("ND_VehicleSystem:returnVehicles", getVehicles(player.id)) local veh = CreateVehicleServerSetter(vehicle.properties.model, getVehicleType(vehicle.properties.model), spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w) while not DoesEntityExist(veh) do Wait(0) end @@ -203,9 +203,19 @@ function NDCore.spawnOwnedVehicle(source, vehicleID, coords) state.owner = vehicle.owner state.id = vehicle.id state.props = vehicle.properties - state.keys = { - [player.id] = true - } + + if Config.useInventoryForKeys then + exports.ox_inventory:AddItem(source, "keys", 1, { + vehOwner = vehicle.owner, + vehId = vehicle.id, + vehPlate = vehicle.properties and vehicle.properties.plate, + vehModel = vehicle.properties and vehicle.properties.model + }) + else + state.keys = { + [player.id] = true + } + end return true end @@ -222,7 +232,7 @@ function NDCore.returnVehicleToGarage(source, veh, properties) for _, vehicle in pairs(vehicles) do if vehicle.owner == player.id and vehicle.id == vehID then MySQL.query.await("UPDATE vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(properties), 1, vehID}) - player:triggerEvent("ND_Vehicles:returnVehicles", getVehicles(player.id)) + player.triggerEvent("ND_Vehicles:returnVehicles", getVehicles(player.id)) DeleteEntity(veh) return true end @@ -261,10 +271,6 @@ if Config.useInventoryForKeys then if not owner or owner ~= player.id then return end local props = state.props - local keys = state.keys - keys[player.id] = true - state.keys = keys - exports.ox_inventory:AddItem(source, "keys", 1, { vehOwner = owner, vehId = state.id, @@ -285,6 +291,6 @@ else if veh == 0 then return end end - giveKeys(veh, src, target) + NDCore.giveVehicleKeys(veh, src, target) end, false) end From c06ca9645784f16a43f56d67f46e5e8149339484 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:28:54 +0200 Subject: [PATCH 026/161] fix: string & return --- client/events.lua | 2 -- client/main.lua | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/client/events.lua b/client/events.lua index bf60e7d..aa31e2f 100644 --- a/client/events.lua +++ b/client/events.lua @@ -17,13 +17,11 @@ end -- Sets main character. RegisterNetEvent("ND:characterLoaded", function(character) - if not NDCore.player then return end NDCore.player = removeCharacterFunctions(character) end) -- Update main character info. RegisterNetEvent("ND:updateCharacter", function(character) - if not NDCore.player then return end NDCore.player = removeCharacterFunctions(character) end) diff --git a/client/main.lua b/client/main.lua index fbe254a..5428b2f 100644 --- a/client/main.lua +++ b/client/main.lua @@ -26,7 +26,7 @@ CreateThread(function() local presenceText = ("Playing: %s"):format(Config.serverName) while true do if NDCore.player then - local presence = ("Playing: %s as % %"):format(Config.serverName, NDCore.player.firstname, NDCore.player.lastname) + local presence = ("Playing: %s as %s %s"):format(Config.serverName, NDCore.player.firstname, NDCore.player.lastname) local presenceTextSmall = ("Playing as: %s %s"):format(NDCore.player.firstname, NDCore.player.lastname) SetRichPresence(presence) SetDiscordRichPresenceAssetText(presenceText) From 87b2451a9303709bc431d1eed29c22424c4070e8 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:29:20 +0200 Subject: [PATCH 027/161] feat: get player on client --- client/functions.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/functions.lua b/client/functions.lua index 4b1e75f..53693c7 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -1,3 +1,7 @@ +function NDCore.getPlayer() + return NDCore.player +end + function NDCore.GetPlayersFromCoords(distance, coords) if coords then coords = type(coords) == "table" and vec3(coords.x, coords.y, coords.z) or coords From 6eb044edc420a5118fe7bd0db9c9c407731040a2 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:30:22 +0200 Subject: [PATCH 028/161] fix(database/characters): license change to identifier --- database/characters.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/characters.sql b/database/characters.sql index 4853fa9..e70a660 100644 --- a/database/characters.sql +++ b/database/characters.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS `nd_characters` ( `charid` INT(10) NOT NULL AUTO_INCREMENT, - `license` VARCHAR(200) NOT NULL DEFAULT '0', + `identifier` VARCHAR(200) NOT NULL DEFAULT '0', `name` VARCHAR(50) DEFAULT NULL, `firstname` VARCHAR(50) DEFAULT NULL, `lastname` VARCHAR(50) DEFAULT NULL, From ee887090435614cfeb5f522c82dc83351e6a5b9d Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:30:35 +0200 Subject: [PATCH 029/161] update: fxmanifest. --- fxmanifest.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index 7632cee..28d155c 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -8,6 +8,7 @@ fx_version "cerulean" game "gta5" lua54 "yes" +shared_script "@ox_lib/init.lua" client_scripts { "client/main.lua", "shared/functions.lua", @@ -23,6 +24,7 @@ server_scripts { "server/vehicle.lua", "server/functions.lua" } -shared_script "init.lua" + +file "init.lua" dependency "oxmysql" From 075807eb51509e62701024309a484607cd5b0580 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:31:02 +0200 Subject: [PATCH 030/161] update: loading sql --- server/functions.lua | 22 ++++++++++++++++++++++ server/main.lua | 16 ++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/server/functions.lua b/server/functions.lua index c136ce7..0e6d9fc 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -22,6 +22,28 @@ function NDCore.getPlayers(key, value) return players end +---@param fileLocation string|tabale +---@return boolean +function NDCore.loadSQL(fileLocation, resource) + local resourceName = resource or GetInvokingResource() or GetCurrentResourceName() + + if type(fileLocation) == "string" then + local file = LoadResourceFile(resourceName, fileLocation) + if not file then return end + MySQL.query(file) + return true + end + + for i=1, #fileLocation do + local file = LoadResourceFile(resourceName, fileLocation[i]) + if file then + MySQL.query(file) + Wait(100) + end + end + return true +end + for name, func in pairs(NDCore) do if type(func) == "function" then exports(name, func) diff --git a/server/main.lua b/server/main.lua index 4fc8470..4042b53 100644 --- a/server/main.lua +++ b/server/main.lua @@ -3,10 +3,6 @@ ActivePlayers = {} PlayersInfo = {} local resourceName = GetCurrentResourceName() local tempPlayersInfo = {} -local databaseFiles = { - "database/characters.sql", - "database/vehicles.sql" -} local discordErrors = { [400] = "Improper HTTP request", [401] = "Discord bot token might be missing or incorrect", @@ -121,9 +117,9 @@ AddEventHandler("playerDropped", function() PlayersInfo[src] = nil end) -for i=1, #databaseFiles do - local file = LoadResourceFile(resourceName, databaseFiles[i]) - if file then - MySQL.query(file) - end -end +SetTimeout(500, function() + NDCore.loadSQL({ + "database/characters.sql", + "database/vehicles.sql" + }, resourceName) +end) From 35726429f93597f45487a31e0c763824a593201e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:31:20 +0200 Subject: [PATCH 031/161] feat: set player data & more --- server/player.lua | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/server/player.lua b/server/player.lua index fb1ad2f..44ec528 100644 --- a/server/player.lua +++ b/server/player.lua @@ -63,8 +63,15 @@ local function createCharacterTable(info) if not amount or self.bank < amount or amount <= 0 then return end return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw") end + + ---@param key any + ---@param value any + function self.setData(key, value) + self[key] = value + self.triggerEvent("ND:updateCharacter", self) + end - ---@param key string + ---@param key any ---@param value any ---@return table function self.setMetadata(key, value) @@ -188,7 +195,8 @@ local function createCharacterTable(info) if char then char.unload() end ActivePlayers[self.source] = self TriggerEvent("ND:characterLoaded", self) - TriggerClientEvent("ND:characterLoaded", self.source, self) + self.triggerEvent("ND:characterLoaded", self) + return true end ---@param name string @@ -243,7 +251,7 @@ function NDCore.newCharacter(src, info) inventory = info.inventory or {}, }) - charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", { + charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, groups, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", { identifier, charInfo.name, charInfo.firstname, @@ -252,6 +260,7 @@ function NDCore.newCharacter(src, info) charInfo.gender, charInfo.cash, charInfo.bank, + json.encode(charInfo.groups), json.encode(charInfo.metadata) }) @@ -290,6 +299,7 @@ function NDCore.fetchAllCharacters(src) for i=1, #result do local info = result[i] characters[info.charid] = createCharacterTable({ + source = src, id = info.charid, identifier = info.identifier, name = info.name, From 9dd5351b32a4c2692a268619661d0a823184d553 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:33:37 +0200 Subject: [PATCH 032/161] fix: character id --- server/player.lua | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/server/player.lua b/server/player.lua index 44ec528..421e1b5 100644 --- a/server/player.lua +++ b/server/player.lua @@ -2,6 +2,7 @@ local function createCharacterTable(info) local playerInfo = PlayersInfo[info.source] or {} local self = { + id = info.id, source = info.source, identifier = info.identifier, identifiers = playerInfo.identifiers or {}, @@ -236,7 +237,7 @@ function NDCore.newCharacter(src, info) local identifier = GetPlayerIdentifierByType(src, Config.characterIdentifier) if not identifier then return end - local charInfo = createCharacterTable({ + local charInfo = { source = src, identifier = identifier, name = GetPlayerName(src) or "", @@ -249,7 +250,7 @@ function NDCore.newCharacter(src, info) groups = info.groups or {}, metadata = info.metadata or {}, inventory = info.inventory or {}, - }) + } charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, groups, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", { identifier, @@ -264,7 +265,7 @@ function NDCore.newCharacter(src, info) json.encode(charInfo.metadata) }) - return charInfo + return createCharacterTable(charInfo) end ---@param id number @@ -275,7 +276,7 @@ function NDCore.fetchCharacter(id) local info = result[1] return createCharacterTable({ - id = id, + id = info.charid, identifier = info.identifier, name = info.name, firstname = info.firstname, @@ -321,15 +322,10 @@ end ---@param id number ---@return table function NDCore.setActiveCharacter(src, id) - local char = ActivePlayers[src] - if char then char.unload() end - + if not src then return end local character = NDCore.fetchCharacter(id) character.source = src character.name = GetPlayerName(src) - ActivePlayers[src] = character - - TriggerEvent("ND:characterLoaded", character) - TriggerClientEvent("ND:characterLoaded", src, character) + character.active() return ActivePlayers[src] end From 01cfcb4b0531b05f3e36b32394dae1b2cd8a61a9 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:33:58 +0200 Subject: [PATCH 033/161] feat: vehicle stuff --- client/vehicle.lua | 130 +++++++++++++++++++++++++++++++++++++++++++++ server/vehicle.lua | 125 ++++++++++++++++++++++++++++--------------- 2 files changed, 213 insertions(+), 42 deletions(-) create mode 100644 client/vehicle.lua diff --git a/client/vehicle.lua b/client/vehicle.lua new file mode 100644 index 0000000..993d471 --- /dev/null +++ b/client/vehicle.lua @@ -0,0 +1,130 @@ +local function getVehicleBlipSprite(entity) + if not IsEntityAVehicle(entity) then + return 148 -- circle blip + end + + local class = GetVehicleClass(entity) + local model = GetEntityModel(entity) + local classBlip = { + [16] = 423, -- plane + [8] = 226, -- motorcycle + [15] = 64, -- helicopter + [14] = 427, -- boat + [6] = 825, -- sports + [7] = 523, -- super + [2] = 821, -- SUV + [4] = 663 -- muscle + } + local typeBlip = { + [`seashark`] = 471, + [`marquis`] = 410, + [`rhino`] = 421, + [`hydra`] = 424, + [`lazer`] = 424, + [`taxi`] = 198, + [`trash`] = 318, + [`trash2`] = 318 + } + + return typeBlip[model] or classBlip[class] or 225 -- 255 is default car blip +end + +RegisterNetEvent("ND_Vehicles:blip", function(netid, status) + local veh = NetToVeh(netid) + if not veh then return end + if not status then + local blip = GetBlipFromEntity(veh) + if not blip or not DoesBlipExist(blip) then return end + return RemoveBlip(blip) + end + local blip = AddBlipForEntity(veh) + SetBlipSprite(blip, getVehicleBlipSprite(veh)) + SetBlipColour(blip, 0) + SetBlipScale(blip, 0.8) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentSubstringPlayerName("Personal vehicle") + EndTextCommandSetBlipName(blip) +end) + +RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid, success, action) + local veh = NetToVeh(netid) + if not veh then return end + SetVehicleAlarmTimeLeft(veh, 1) + SetVehicleAlarm(veh, true) + StartVehicleAlarm(veh) + if success and action == "lockpick" then + setVehicleLocked(veh, false) + end +end) + +RegisterNetEvent("ND_VehicleSystem:setOwnedIfNot", function(netid) + local veh = NetToVeh(netid) + if not veh then return end + setVehicleOwned(veh, true) + setVehicleLocked(veh, true) +end) + +AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, replicated) + local entity = GetEntityFromStateBagName(bagName) + if entity == 0 or not value then return end + lib.setVehicleProperties(entity, value) +end) + +local function playKeyFob(veh) + local keyFob + local ped = cache.ped + if GetVehiclePedIsIn(ped) == 0 then + ClearPedTasks(ped) + lib.requestAnimDict("anim@mp_player_intmenu@key_fob@") + TaskPlayAnim(ped, "anim@mp_player_intmenu@key_fob@", "fob_click_fp", 8.0, 8.0, -1, 48, 1, false, false, false) + keyFob = CreateObject(`lr_prop_carkey_fob`, 0, 0, 0, true, true, true) + AttachEntityToEntity(keyFob, ped, GetPedBoneIndex(ped, 0xDEAD), 0.12, 0.04, -0.025, -100.0, 100.0, 0.0, true, true, false, true, 1, true) + Wait(700) + end + + PlaySoundFromEntity(-1, "Remote_Control_Fob", ped, "PI_Menu_Sounds", true, 0) + SetVehicleLights(veh, 2) + Wait(100) + SetVehicleLights(veh, 0) + Wait(200) + SetVehicleLights(veh, 2) + Wait(100) + SetVehicleLights(veh, 0) + return keyFob and DeleteEntity(keyFob) +end + +AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, replicated) + local entity = GetEntityFromStateBagName(bagName) + if entity == 0 then return end + + CreateThread(function() + playKeyFob(entity) + end) + + if value then + SetVehicleDoorsLocked(entity, 4) + return SetVehicleDoorsLocked(entity, 2) + end + + CreateThread(function() + while GetVehiclePedIsEntering(cache.ped) ~= 0 do Wait(10) end + SetVehicleDoorsLocked(entity, 1) + end) +end) + +lib.callback.register("ND_Vehicles:getProps", function() + return lib.getVehicleProperties(GetVehiclePedIsIn(cache.ped)) +end) + +lib.callback.register("ND_Vehicles:getNearbyVehicleById", function(vehId) + local coords = GetEntityCoords(cache.ped) + local vehicles = lib.getNearbyVehicles(coords, 25.0, true) + for i=1, #vehicles do + local veh = vehicles[i] + local state = Entity(veh).state + if state and state.id == vehId then + return VehToNet(veh) + end + end +end) diff --git a/server/vehicle.lua b/server/vehicle.lua index d824cc5..9f77ecb 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -1,7 +1,7 @@ local playerOwnedVehicles = {} local function isPlateAvailable(plate) - return not MySQL.scalar.await("SELECT 1 FROM vehicles WHERE plate = ?", {plate}) + return not MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) end local function generatePlate() @@ -21,7 +21,7 @@ local function generatePlateWait() end local function getVehicles(characterId) - local result = MySQL.query.await("SELECT * FROM vehicles WHERE owner = ?", {characterId}) + local result = MySQL.query.await("SELECT * FROM nd_vehicles WHERE owner = ?", {characterId}) if not result then return {} end local vehicles = {} for _, vehicle in pairs(result) do @@ -89,7 +89,7 @@ function NDCore.setVehicleOwned(src, properties, stored) local player = NDCore.getPlayer(src) local plate = generatePlateWait() properties.plate = plate - local id = MySQL.insert.await("INSERT INTO vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {player.id, properties.plate, json.encode(properties), stored and 1 or 0}) + local id = MySQL.insert.await("INSERT INTO nd_vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {player.id, properties.plate, json.encode(properties), stored and 1 or 0}) local vehicles = getVehicles(player.id) player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) return id @@ -146,63 +146,37 @@ function NDCore.giveVehicleAccess(source, vehicle, access) player.triggerEvent("ND_Vehicles:setOwnedIfNot", NetworkGetNetworkIdFromEntity(vehicle)) end -local function isParkingAvailable(source, coords) - local tries = 1 - while tries < #coords do - Wait(300) - local coord = coords[math.random(1, #coords)] - local available = lib.callback.await("ND_Vehicles:getParkedVehicle", source, vector3(coord.x, coord.y, coord.z)) - if available then - return coord - end - tries += 1 - end - return false -end - local function getVehicleType(model) - local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, false, false) + local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, true, true) while not DoesEntityExist(tempVehicle) do Wait(0) end local entityType = GetVehicleType(tempVehicle) DeleteEntity(tempVehicle) return entityType end -function NDCore.spawnOwnedVehicle(source, vehicleID, coords) +function NDCore.spawnOwnedVehicle(source, vehicleID, coords, heading) local player = NDCore.getPlayer(source) - local spawnCoords = coords - - if type(coords) == "table" then - spawnCoords = isParkingAvailable(source, coords) - if not spawnCoords then - player.notify({ - title = "Can't bring out vehicle", - description = "No parking spot available for your vehicle. It's still in your garage.", - type = "error", - position = "bottom", - duration = 4000 - }) - return - end - end - local vehicles = getVehicles(player.id) for _, vehicle in pairs(vehicles) do - if vehicle.owner == player.id and vehicle.id == vehicleID then - MySQL.query.await("UPDATE vehicles SET stored = ? WHERE id = ?", {0, vehicleID}) - player.triggerEvent("ND_VehicleSystem:returnVehicles", getVehicles(player.id)) + if vehicle.id == vehicleID and vehicle.owner == player.id then + MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleID}) + vehicle.available = false + player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) - local veh = CreateVehicleServerSetter(vehicle.properties.model, getVehicleType(vehicle.properties.model), spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w) + local model = vehicle.properties.model + local veh = CreateVehicleServerSetter(model, getVehicleType(model), coords.x, coords.y, coords.z, coords.w or heading) while not DoesEntityExist(veh) do Wait(0) end playerOwnedVehicles[vehicle.id] = { - netid = NetworkGetNetworkIdFromEntity(veh) + netid = NetworkGetNetworkIdFromEntity(veh), + entity = veh } local state = Entity(veh).state state.owner = vehicle.owner state.id = vehicle.id state.props = vehicle.properties + state.locked = true if Config.useInventoryForKeys then exports.ox_inventory:AddItem(source, "keys", 1, { @@ -231,7 +205,7 @@ function NDCore.returnVehicleToGarage(source, veh, properties) for _, vehicle in pairs(vehicles) do if vehicle.owner == player.id and vehicle.id == vehID then - MySQL.query.await("UPDATE vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(properties), 1, vehID}) + MySQL.query.await("UPDATE nd_vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(properties), 1, vehID}) player.triggerEvent("ND_Vehicles:returnVehicles", getVehicles(player.id)) DeleteEntity(veh) return true @@ -248,7 +222,7 @@ function NDCore.saveVehicleProperties(source, veh, properties) for _, vehicle in pairs(vehicles) do if vehicle.owner == player.id and vehicle.id == vehID then - MySQL.query.await("UPDATE vehicles SET properties = ? WHERE id = ?", {json.encode(properties), vehID}) + MySQL.query.await("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), vehID}) return true end end @@ -261,6 +235,73 @@ RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid, success, action) end) if Config.useInventoryForKeys then + local function toggleVehicleLock(id, veh, nearby) + local player = NDCore.getPlayer(id) + if nearby then + local state = Entity(veh).state + local locked = not state.locked + state.locked = locked + if locked then + player.notify({ + title = "LOCKED", + description = "Your vehicle has now been locked.", + type = "success", + position = "bottom-right", + duration = 3000 + }) + return + end + player.notify({ + title = "UNLOCKED", + description = "Your vehicle has now been unlocked.", + type = "inform", + position = "bottom-right", + duration = 3000 + }) + return + end + player.notify({ + title = "No signal", + description = "Vehicle to far away.", + type = "error", + position = "bottom-right", + duration = 3000 + }) + end + + + exports("keys", function(event, item, inventory, slot, data) + if event ~= "usingItem" then return end + local metadata + for i=1, #inventory.items do + local item = inventory.items[i] + if item.slot == slot then + metadata = item.metadata + break + end + end + + if not metadata then return false end + local veh = playerOwnedVehicles[metadata.vehId] and playerOwnedVehicles[metadata.vehId].entity + if veh and DoesEntityExist(veh) then + local ped = GetPlayerPed(inventory.id) + local pedCoords = GetEntityCoords(ped) + local vehCoords = GetEntityCoords(veh) + if not pedCoords or not vehCoords then return end + toggleVehicleLock(inventory.id, veh, #(pedCoords-vehCoords) < 25.0) + return false + end + + lib.callback("ND_Vehicles:getNearbyVehicleById", inventory.id, function(netId) + if not netId then return end + local veh = NetworkGetEntityFromNetworkId(netId) + if veh then + toggleVehicleLock(inventory.id, veh, true) + end + end, metadata.vehId) + return false + end) + RegisterCommand("getkeys", function(source, args, rawCommand) local veh = GetVehiclePedIsIn(GetPlayerPed(source)) if not veh or veh == 0 then return end From 5fd51f5329719caf724ed9a1bfcff63126e2396c Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:42:06 +0200 Subject: [PATCH 034/161] fix: issues with key item --- server/vehicle.lua | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 9f77ecb..d6f3b2c 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -156,6 +156,7 @@ end function NDCore.spawnOwnedVehicle(source, vehicleID, coords, heading) local player = NDCore.getPlayer(source) + if not player then return end local vehicles = getVehicles(player.id) for _, vehicle in pairs(vehicles) do if vehicle.id == vehicleID and vehicle.owner == player.id then @@ -178,13 +179,15 @@ function NDCore.spawnOwnedVehicle(source, vehicleID, coords, heading) state.props = vehicle.properties state.locked = true - if Config.useInventoryForKeys then - exports.ox_inventory:AddItem(source, "keys", 1, { - vehOwner = vehicle.owner, - vehId = vehicle.id, - vehPlate = vehicle.properties and vehicle.properties.plate, - vehModel = vehicle.properties and vehicle.properties.model - }) + if GetResourceState("ox_inventory") == "started" and Config.useInventoryForKeys then + if exports.ox_inventory:GetItem(source, "keys", {vehId = vehicle.id}, true) == 0 then + exports.ox_inventory:AddItem(source, "keys", 1, { + vehOwner = vehicle.owner, + vehId = vehicle.id, + vehPlate = vehicle.properties and vehicle.properties.plate, + vehModel = vehicle.properties and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, vehicle.properties.model) or "" + }) + end else state.keys = { [player.id] = true @@ -303,6 +306,7 @@ if Config.useInventoryForKeys then end) RegisterCommand("getkeys", function(source, args, rawCommand) + if GetResourceState("ox_inventory") ~= "started" then return end local veh = GetVehiclePedIsIn(GetPlayerPed(source)) if not veh or veh == 0 then return end @@ -316,7 +320,7 @@ if Config.useInventoryForKeys then vehOwner = owner, vehId = state.id, vehPlate = props.plate, - vehModel = props.model + vehModel = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, props.model) }) end, false) else From 98433908e91f48a6f5da14b05110475ed7514988 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:42:45 +0200 Subject: [PATCH 035/161] update: keyfob anim --- client/vehicle.lua | 17 +++++++++++++---- server/vehicle.lua | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 993d471..9401784 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -74,6 +74,9 @@ end) local function playKeyFob(veh) local keyFob local ped = cache.ped + local coords = GetEntityCoords(ped) + if #(coords-GetEntityCoords(veh)) > 25.0 then return end + if GetVehiclePedIsIn(ped) == 0 then ClearPedTasks(ped) lib.requestAnimDict("anim@mp_player_intmenu@key_fob@") @@ -94,13 +97,13 @@ local function playKeyFob(veh) return keyFob and DeleteEntity(keyFob) end +RegisterNetEvent("ND_Vehicles:keyFob", function(vehicleNetId) + playKeyFob(NetToVeh(vehicleNetId)) +end) + AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, replicated) local entity = GetEntityFromStateBagName(bagName) if entity == 0 then return end - - CreateThread(function() - playKeyFob(entity) - end) if value then SetVehicleDoorsLocked(entity, 4) @@ -128,3 +131,9 @@ lib.callback.register("ND_Vehicles:getNearbyVehicleById", function(vehId) end end end) + +lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) + local make = GetLabelText(GetMakeNameFromVehicleModel(model)) + local name = GetLabelText(GetDisplayNameFromVehicleModel(model)) + return ("%s %s"):format(make, name) +end) diff --git a/server/vehicle.lua b/server/vehicle.lua index d6f3b2c..99f6c36 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -244,6 +244,7 @@ if Config.useInventoryForKeys then local state = Entity(veh).state local locked = not state.locked state.locked = locked + player.triggerEvent("ND_Vehicles:keyFob", NetworkGetNetworkIdFromEntity(veh)) if locked then player.notify({ title = "LOCKED", From 01500fe081043642461cff3aecf5d6b7b9d41b6d Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:43:04 +0200 Subject: [PATCH 036/161] update: key item metadata --- client/vehicle.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 9401784..4b9656a 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -137,3 +137,11 @@ lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) local name = GetLabelText(GetDisplayNameFromVehicleModel(model)) return ("%s %s"):format(make, name) end) + +SetTimeout(1000, function() + if GetResourceState("ox_inventory") ~= "started" or not Config.useInventoryForKeys then return end + exports.ox_inventory:displayMetadata({ + vehPlate = "Plate", + vehModel = "Model" + }) +end) From d29d73c075b6a7d7d07e677a3e3721e9f5ad59d4 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:43:22 +0200 Subject: [PATCH 037/161] update: vehicle locking --- client/vehicle.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 4b9656a..d698477 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -101,18 +101,26 @@ RegisterNetEvent("ND_Vehicles:keyFob", function(vehicleNetId) playKeyFob(NetToVeh(vehicleNetId)) end) +local dontLock = { + [8] = true, -- Motorcycles + [13] = true, -- Cycles + [14] = true -- boats +} + AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, replicated) local entity = GetEntityFromStateBagName(bagName) if entity == 0 then return end + if dontLock[GetVehicleClass(entity)] then return end if value then - SetVehicleDoorsLocked(entity, 4) + -- SetVehicleDoorsLockedForAllPlayers(entity, true) return SetVehicleDoorsLocked(entity, 2) end CreateThread(function() - while GetVehiclePedIsEntering(cache.ped) ~= 0 do Wait(10) end - SetVehicleDoorsLocked(entity, 1) + while GetVehiclePedIsEntering(cache.ped) == entity do Wait(10) end + -- SetVehicleDoorsLockedForAllPlayers(entity, false) + SetVehicleDoorsLocked(entity, 0) end) end) From bc7e2ddd2a78cb7c36f50d8f8358fa22f5d22c87 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:43:36 +0200 Subject: [PATCH 038/161] update: lock traffic vehicles --- server/vehicle.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/vehicle.lua b/server/vehicle.lua index 99f6c36..3fca885 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -340,3 +340,10 @@ else NDCore.giveVehicleKeys(veh, src, target) end, false) end +RegisterNetEvent("entityCreated", function(entity) + if GetEntityType(entity) ~= 2 then return end + local state = Entity(entity).state + if state.owner or math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end + if state.locked ~= nil then return end + state.locked = true +end) From eaba11e4e36d05e38c34e55524404ee35ebcaa51 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 23:59:11 +0200 Subject: [PATCH 039/161] feat: get player server info function --- server/functions.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/functions.lua b/server/functions.lua index 0e6d9fc..4177d0a 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -22,6 +22,12 @@ function NDCore.getPlayers(key, value) return players end +---@param source number +---@return table +function NDCore.getPlayerServerInfo(source) + return PlayersInfo[source] +end + ---@param fileLocation string|tabale ---@return boolean function NDCore.loadSQL(fileLocation, resource) From a68b6657e56e678f4e77eaf13d694a1a0cd6be2f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 22 Jul 2023 23:59:39 +0200 Subject: [PATCH 040/161] feat: job player functions --- server/player.lua | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/server/player.lua b/server/player.lua index 421e1b5..e3db8a4 100644 --- a/server/player.lua +++ b/server/player.lua @@ -203,13 +203,19 @@ local function createCharacterTable(info) ---@param name string ---@param rank number ---@return boolean - function self.addGroup(name, rank) + function self.addGroup(name, rank, isJob) local groupInfo = Config.groups[name] if not groupInfo then return end + if isJob then + for _, group in pairs(self.groups) do + group.isJob = nil + end + end self.groups[name] = { label = groupInfo.label, rankName = groupInfo.ranks[rank] or rank, - rank = rank + rank = rank, + isJob = isJob } self.triggerEvent("ND:updateCharacter", self) return true @@ -227,6 +233,23 @@ local function createCharacterTable(info) self.triggerEvent("ND:updateCharacter", self) end + ---@param name string + ---@param rank number + ---@return boolean + function self.setJob(name, rank) + return self.addGroup(name, rank, true) + end + + ---@param job string + ---@return boolean + function self.getJob(job) + for name, group in pairs(self.groups) do + if group.isJob and not job or name == job then + return group + end + end + end + return self end From f3d54a3a2759bf5d0c2bc024bf84c79aef3ae067 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 23 Jul 2023 00:00:08 +0200 Subject: [PATCH 041/161] feat: set player data and player metadata by table --- server/player.lua | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/server/player.lua b/server/player.lua index e3db8a4..ecc16b9 100644 --- a/server/player.lua +++ b/server/player.lua @@ -65,18 +65,30 @@ local function createCharacterTable(info) return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw") end - ---@param key any + ---@param key string|table ---@param value any function self.setData(key, value) - self[key] = value + if type(key) == "table" then + for k, v in pairs(key) do + self[k] = v + end + else + self[key] = value + end self.triggerEvent("ND:updateCharacter", self) end - ---@param key any + ---@param key string|table ---@param value any ---@return table function self.setMetadata(key, value) - self.metadata[key] = value + if type(key) == "table" then + for k, v in pairs(key) do + self.metadata[k] = v + end + else + self.metadata[key] = value + end self.triggerEvent("ND:updateCharacter", self) return self.metadata end @@ -202,6 +214,7 @@ local function createCharacterTable(info) ---@param name string ---@param rank number + ---@param isJob boolean ---@return boolean function self.addGroup(name, rank, isJob) local groupInfo = Config.groups[name] From 44066b0828932078b3db3c23a3e9afc50183ba37 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 23 Jul 2023 23:24:36 +0200 Subject: [PATCH 042/161] update: fetch with identifer and charid --- server/player.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/server/player.lua b/server/player.lua index ecc16b9..4a50e4b 100644 --- a/server/player.lua +++ b/server/player.lua @@ -306,12 +306,18 @@ end ---@param id number ---@return table -function NDCore.fetchCharacter(id) - local result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id}) +function NDCore.fetchCharacter(id, src) + local result + if src then + result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ? and identifier = ?", {id, GetPlayerIdentifierByType(src, Config.characterIdentifier)}) + else + result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id}) + end + if not result then return end - local info = result[1] return createCharacterTable({ + source = src, id = info.charid, identifier = info.identifier, name = info.name, @@ -359,8 +365,7 @@ end ---@return table function NDCore.setActiveCharacter(src, id) if not src then return end - local character = NDCore.fetchCharacter(id) - character.source = src + local character = NDCore.fetchCharacter(id, src) character.name = GetPlayerName(src) character.active() return ActivePlayers[src] From 47c74409ca0cd92ec6e8782cfe7ac287e3fb433e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 19 Aug 2023 22:07:41 +0200 Subject: [PATCH 043/161] update: discord identifier & player info --- server/main.lua | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/server/main.lua b/server/main.lua index 4042b53..3e57f36 100644 --- a/server/main.lua +++ b/server/main.lua @@ -74,6 +74,27 @@ AddEventHandler("playerJoining", function(oldId) tempPlayersInfo[oldId] = nil end) +local function checkDiscordIdentifier(identifiers) + if not Config.discordBotToken or not Config.discordGuildId then return end + + local discordIdentifier = identifiers["discord"] + if not discordIdentifier then return end + + return getDiscordInfo(discordIdentifier:gsub("discord:", "")) +end + +AddEventHandler("onResourceStart", function(name) + if name ~= resourceName or not Config.discordBotToken or not Config.discordGuildId then return end + for _, playerId in ipairs(GetPlayers()) do + local src = tonumber(playerId) + local identifiers = getIdentifierList(src) + PlayersInfo[src] = { + identifiers = identifiers, + discord = checkDiscordIdentifier(identifiers) + } + end +end) + AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) local tempSrc = source local identifiers = getIdentifierList(tempSrc) @@ -83,14 +104,12 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) deferrals.defer() Wait(0) - if mainIdentifier and Config.discordBotToken and Config.discordGuildId then - local discordIdentifier = identifiers["discord"] - if not discordIdentifier then + if mainIdentifier then + discordInfo = checkDiscordIdentifier(identifiers) + if not discordInfo then deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite)) Wait(0) - return end - discordInfo = getDiscordInfo(discordIdentifier:gsub("discord:", "")) end deferrals.update("Connecting...") From 38680c7abf0917bfa1e3ccc87e3afffdd3972538 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 19 Aug 2023 22:09:23 +0200 Subject: [PATCH 044/161] update: group & job --- server/player.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/server/player.lua b/server/player.lua index 4a50e4b..5bfc010 100644 --- a/server/player.lua +++ b/server/player.lua @@ -217,17 +217,19 @@ local function createCharacterTable(info) ---@param isJob boolean ---@return boolean function self.addGroup(name, rank, isJob) + local groupRank = tonumber(rank) or 1 local groupInfo = Config.groups[name] if not groupInfo then return end if isJob then + self.job = name for _, group in pairs(self.groups) do group.isJob = nil end end self.groups[name] = { label = groupInfo.label, - rankName = groupInfo.ranks[rank] or rank, - rank = rank, + rankName = groupInfo.ranks[groupRank] or groupRank, + rank = groupRank, isJob = isJob } self.triggerEvent("ND:updateCharacter", self) @@ -242,6 +244,9 @@ local function createCharacterTable(info) ---@param name string function self.removeGroup(name) + if self.job == "name" then + self.job = nil + end self.groups[name] = nil self.triggerEvent("ND:updateCharacter", self) end @@ -257,7 +262,7 @@ local function createCharacterTable(info) ---@return boolean function self.getJob(job) for name, group in pairs(self.groups) do - if group.isJob and not job or name == job then + if group.isJob and not job or group.isJob and name == job then return group end end From 27d6ce4f9c2221463fcac31aae8deaf2074b3aca Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 19 Aug 2023 22:38:03 +0200 Subject: [PATCH 045/161] Update vehicle.lua --- client/vehicle.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index d698477..6b6e5a1 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -146,8 +146,9 @@ lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) return ("%s %s"):format(make, name) end) -SetTimeout(1000, function() - if GetResourceState("ox_inventory") ~= "started" or not Config.useInventoryForKeys then return end +AddEventHandler("onResourceStart", function(resourceName) + if resourceName ~= "ox_inventory" or not Config.useInventoryForKeys then return end + Wait(1000) exports.ox_inventory:displayMetadata({ vehPlate = "Plate", vehModel = "Model" From ab15847a327980102e7b9eed5f5d48c00236a228 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:30:38 +0200 Subject: [PATCH 046/161] update: ActivePlayers to NDCore.players --- server/functions.lua | 6 +++--- server/main.lua | 7 ++++--- server/player.lua | 24 +++++++++++++++--------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/server/functions.lua b/server/functions.lua index 4177d0a..1a34ecf 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -1,20 +1,20 @@ ---@param src number ---@return table function NDCore.getPlayer(src) - return ActivePlayers[src] + return NDCore.players[src] end ---@param metadata string ---@param data any ---@return table function NDCore.getPlayers(key, value) - if not key or not value then return ActivePlayers end + if not key or not value then return NDCore.players end local players = {} local keyTypes = {firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"} local findBy = keyTypes[key] or "metadata" - for src, info in pairs(ActivePlayers) do + for src, info in pairs(NDCore.players) do if info[findBy][key] == value then players[src] = info end diff --git a/server/main.lua b/server/main.lua index 3e57f36..bde831b 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,5 +1,5 @@ NDCore = {} -ActivePlayers = {} +NDCore.players = {} PlayersInfo = {} local resourceName = GetCurrentResourceName() local tempPlayersInfo = {} @@ -80,7 +80,7 @@ local function checkDiscordIdentifier(identifiers) local discordIdentifier = identifiers["discord"] if not discordIdentifier then return end - return getDiscordInfo(discordIdentifier:gsub("discord:", "")) + return NDCore.getDiscordInfo(discordIdentifier:gsub("discord:", "")) end AddEventHandler("onResourceStart", function(name) @@ -92,6 +92,7 @@ AddEventHandler("onResourceStart", function(name) identifiers = identifiers, discord = checkDiscordIdentifier(identifiers) } + Wait(65) end end) @@ -129,7 +130,7 @@ end) AddEventHandler("playerDropped", function() local src = source - local char = ActivePlayers[src] + local char = NDCore.players[src] if char then char.unload() end diff --git a/server/player.lua b/server/player.lua index 5bfc010..90d73f9 100644 --- a/server/player.lua +++ b/server/player.lua @@ -27,7 +27,7 @@ local function createCharacterTable(info) local amount = tonumber(amount) if not amount or account ~= "bank" and account ~= "cash" then return end self[account] -= amount - if ActivePlayers[self.source] then + if NDCore.players[self.source] then self.triggerEvent("ND:updateMoney", self.cash, self.bank) TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason) end @@ -42,7 +42,7 @@ local function createCharacterTable(info) local amount = tonumber(amount) if not amount or account ~= "bank" and account ~= "cash" then return end self[account] += amount - if ActivePlayers[self.source] then + if NDCore.players[self.source] then self.triggerEvent("ND:updateMoney", self.cash, self.bank) TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason) end @@ -71,9 +71,15 @@ local function createCharacterTable(info) if type(key) == "table" then for k, v in pairs(key) do self[k] = v + if k == "cash" or k == "bank" then + TriggerEvent("ND:moneyChange", self.source, k, v, "set") + end end else self[key] = value + if key == "cash" or key == "bank" then + TriggerEvent("ND:moneyChange", self.source, key, value, "set") + end end self.triggerEvent("ND:updateCharacter", self) end @@ -96,15 +102,15 @@ local function createCharacterTable(info) -- Completely delete character function self.delete() local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id}) - if result and ActivePlayers[self.source] then - ActivePlayers[self.source] = nil + if result and NDCore.players[self.source] then + NDCore.players[self.source] = nil end return result end -- Unload and save character function self.unload() - if not ActivePlayers[self.source] then return end + if not NDCore.players[self.source] then return end local ped = GetPlayerPed(self.source) if ped then local coords = GetEntityCoords(ped) @@ -118,7 +124,7 @@ local function createCharacterTable(info) end TriggerEvent("ND:characterUnloaded", self.source, self) self.save() - ActivePlayers[self.source] = nil + NDCore.players[self.source] = nil end -- Save character information to database @@ -203,10 +209,10 @@ local function createCharacterTable(info) -- Set the character as the players active character/currently playing character function self.active() - local char = ActivePlayers[self.source] + local char = NDCore.players[self.source] if char and char.id == self.id then return true end if char then char.unload() end - ActivePlayers[self.source] = self + NDCore.players[self.source] = self TriggerEvent("ND:characterLoaded", self) self.triggerEvent("ND:characterLoaded", self) return true @@ -373,5 +379,5 @@ function NDCore.setActiveCharacter(src, id) local character = NDCore.fetchCharacter(id, src) character.name = GetPlayerName(src) character.active() - return ActivePlayers[src] + return character end From cf67d9a4e215ebfa6fad2757b975d65a70b8b1eb Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:32:49 +0200 Subject: [PATCH 047/161] update: discord function to core object func --- server/functions.lua | 32 ++++++++++++++++++++++++++++++++ server/main.lua | 29 ----------------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/server/functions.lua b/server/functions.lua index 1a34ecf..6721992 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -50,6 +50,38 @@ function NDCore.loadSQL(fileLocation, resource) return true end +function NDCore.getDiscordInfo(discordUserId) + local done = false + local data + local discordErrors = { + [400] = "Improper HTTP request", + [401] = "Discord bot token might be missing or incorrect", + [404] = "User might not be in the server", + [429] = "Discord bot rate limited" + } + + if not discordUserId then return end + if type(discordUserId) == "string" and discordUserId:find("discord:") then discordUserId:gsub("discord:", "") end + + PerformHttpRequest(("https://discordapp.com/api/guilds/%s/members/%s"):format(Config.discordGuildId, discordUserId), function(errorCode, resultData, resultHeaders) + if errorCode ~= 200 then + done = true + return print(("^3Warning: %d %s"):format(errorCode, discordErrors[errorCode])) + end + + local result = json.decode(resultData) + data = { + nickname = result.nick or result.user.username, + user = result.user, + roles = result.roles + } + done = true + end, "GET", "", {["Content-Type"] = "application/json", ["Authorization"] = ("Bot %s"):format(Config.discordBotToken)}) + + while not done do Wait(50) end + return data +end + for name, func in pairs(NDCore) do if type(func) == "function" then exports(name, func) diff --git a/server/main.lua b/server/main.lua index bde831b..11885ae 100644 --- a/server/main.lua +++ b/server/main.lua @@ -3,12 +3,6 @@ NDCore.players = {} PlayersInfo = {} local resourceName = GetCurrentResourceName() local tempPlayersInfo = {} -local discordErrors = { - [400] = "Improper HTTP request", - [401] = "Discord bot token might be missing or incorrect", - [404] = "User might not be in the server", - [429] = "Discord bot rate limited" -} Config = { serverName = GetConvar("core:serverName", "Unconfigured ND-Core Server"), @@ -45,29 +39,6 @@ local function getIdentifierList(src) return list end -local function getDiscordInfo(discordUserId) - local done = false - local data - - PerformHttpRequest(("https://discordapp.com/api/guilds/%s/members/%s"):format(Config.discordGuildId, discordUserId), function(errorCode, resultData, resultHeaders) - if errorCode ~= 200 then - done = true - return print(("^3Warning: %d %s"):format(errorCode, discordErrors[errorCode])) - end - - local result = json.decode(resultData) - data = { - nickname = result.nick or result.user.username, - user = result.user, - roles = result.roles - } - done = true - end, "GET", "", {["Content-Type"] = "application/json", ["Authorization"] = ("Bot %s"):format(Config.discordBotToken)}) - - while not done do Wait(500) end - return data -end - AddEventHandler("playerJoining", function(oldId) local src = source PlayersInfo[src] = tempPlayersInfo[oldId] From 26ae7ea1a32008b5f0b9a544b4add5c60c5c6304 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:33:06 +0200 Subject: [PATCH 048/161] feat: add ace perms on group change --- server/player.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/player.lua b/server/player.lua index 90d73f9..a7a44bc 100644 --- a/server/player.lua +++ b/server/player.lua @@ -239,6 +239,7 @@ local function createCharacterTable(info) isJob = isJob } self.triggerEvent("ND:updateCharacter", self) + ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.identifier, name)) return true end @@ -255,6 +256,7 @@ local function createCharacterTable(info) end self.groups[name] = nil self.triggerEvent("ND:updateCharacter", self) + ExecuteCommand(("remove_principal identifier.%s group.%s"):format(self.identifier, name)) end ---@param name string From 0a5ddfe1a85dafc26e0a03ef0b5848ce0ae386ba Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:33:21 +0200 Subject: [PATCH 049/161] fix: check if money is below 0 --- server/player.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/player.lua b/server/player.lua index a7a44bc..eface4f 100644 --- a/server/player.lua +++ b/server/player.lua @@ -25,7 +25,7 @@ local function createCharacterTable(info) ---@return boolean function self.deductMoney(account, amount, reason) local amount = tonumber(amount) - if not amount or account ~= "bank" and account ~= "cash" then return end + if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end self[account] -= amount if NDCore.players[self.source] then self.triggerEvent("ND:updateMoney", self.cash, self.bank) @@ -40,7 +40,7 @@ local function createCharacterTable(info) ---@return boolean function self.addMoney(account, amount, reason) local amount = tonumber(amount) - if not amount or account ~= "bank" and account ~= "cash" then return end + if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end self[account] += amount if NDCore.players[self.source] then self.triggerEvent("ND:updateMoney", self.cash, self.bank) From 76b93097f62b6c043817332650bb53d2d432570e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:34:03 +0200 Subject: [PATCH 050/161] feat: display ox inventory key metadata --- client/vehicle.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 6b6e5a1..0cd26f0 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -154,3 +154,10 @@ AddEventHandler("onResourceStart", function(resourceName) vehModel = "Model" }) end) + +if GetResourceState("ox_inventory") == "started" then + exports.ox_inventory:displayMetadata({ + vehPlate = "Plate", + vehModel = "Model" + }) +end From 1652dc4b5c86ad1a09342ee4544410bb85f6d627 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:34:12 +0200 Subject: [PATCH 051/161] update: client functions --- client/functions.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/functions.lua b/client/functions.lua index 53693c7..bd18218 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -2,7 +2,11 @@ function NDCore.getPlayer() return NDCore.player end -function NDCore.GetPlayersFromCoords(distance, coords) +function NDCore.getCharacters() + return NDCore.characters +end + +function NDCore.getPlayersFromCoords(distance, coords) if coords then coords = type(coords) == "table" and vec3(coords.x, coords.y, coords.z) or coords else From 3ec20ea087e4f98bd912240d35399cff9b606a62 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:34:41 +0200 Subject: [PATCH 052/161] feat: NDCore 1 backwards compatibility --- compatibility/backwards/client.lua | 16 ++ compatibility/backwards/server.lua | 260 +++++++++++++++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 compatibility/backwards/client.lua create mode 100644 compatibility/backwards/server.lua diff --git a/compatibility/backwards/client.lua b/compatibility/backwards/client.lua new file mode 100644 index 0000000..9275a7a --- /dev/null +++ b/compatibility/backwards/client.lua @@ -0,0 +1,16 @@ +NDCore.Functions = {} +NDCore.Functions.GetSelectedCharacter = NDCore.getPlayer +NDCore.Functions.GetCharacters = NDCore.getCharacters +NDCore.Functions.GetPlayersFromCoords = NDCore.getPlayersFromCoords + +exports("GetCoreObject", function() + return NDCore +end) + +RegisterNetEvent("ND:returnCharacters", function(characters) + NDCore.characters = characters +end) + +RegisterNetEvent("ND:setCharacter", function(character) + NDCore.player = character +end) diff --git a/compatibility/backwards/server.lua b/compatibility/backwards/server.lua new file mode 100644 index 0000000..492a71c --- /dev/null +++ b/compatibility/backwards/server.lua @@ -0,0 +1,260 @@ +exports("GetCoreObject", function() + return NDCore +end) + +NDCore.Functions = {} +NDCore.Functions.GetPlayer = NDCore.getPlayer +NDCore.Functions.GetPlayers = NDCore.getPlayers +NDCore.Functions.GetUserDiscordInfo = NDCore.getDiscordInfo +NDCore.Functions.SetActiveCharacter = NDCore.setActiveCharacter +NDCore.Functions.GetPlayerCharacters = NDCore.fetchAllCharacters +NDCore.Functions.GetPlayerByCharacterId = NDCore.fetchCharacter + +function NDCore.Functions.GetPlayerIdentifierFromType(identifierType, src) + return GetPlayerIdentifierByType(src, identifierType) +end + +function NDCore.Functions.GetNearbyPedToPlayer(src) + local pedCoords = GetEntityCoords(GetPlayerPed(src)) + for targetId, targetInfo in pairs(NDCore.players) do + local targetCoords = GetEntityCoords(GetPlayerPed(targetId)) + if #(pedCoords - targetCoords) < 2.0 and targetId ~= src then + return targetId, targetInfo + end + end +end + +function NDCore.Functions.UpdateMoney(src) + local player = NDCore.getPlayer(src) + player.triggerEvent("ND:updateMoney", player.cash, player.bank) +end + +function NDCore.Functions.TransferBank(amount, source, target, descriptionSender, descriptionReceiver) + local amount = tonumber(amount) + local src = tonumber(source) + local target = tonumber(target) + local player = NDCore.getPlayer(src) + if not player then return end + + if src == target then + return false, "Can't transfer money to same account" + elseif GetPlayerPing(target) == 0 then + return false, "Account not found" + elseif amount <= 0 then + return false, "Invalid amount" + elseif player.bank < amount then + return false, "Insufficient funds" + end + + local targetPlayer = NDCore.getPlayer(target) + if not targetPlayer then return end + return player.deductMoney("bank", amount, descriptionSender or "Transfer") and targetPlayer.addMoney("bank", amount, descriptionReceiver or "Transfer") +end + +function NDCore.Functions.GiveCash(amount, source, target) + local amount = tonumber(amount) + local src = tonumber(source) + local target = tonumber(target) + local player = NDCore.getPlayer(src) + if not player then return end + + if src == target then + return false, "Can't give to self" + elseif GetPlayerPing(target) == 0 then + return false, "Target not found" + elseif amount <= 0 then + return false, "Invalid amount" + elseif player.bank < amount then + return false, "Not enough money" + end + + local targetPlayer = NDCore.getPlayer(target) + if not targetPlayer then return endn end + return player.deductMoney("cash", amount) and targetPlayer.addMoney("cash", amount) +end + +function NDCore.Functions.GiveCashToNearbyPlayer(source, amount) + local targetId = NDCore.Functions.GetNearbyPedToPlayer(source) + if not targetId then return end + return NDCore.Functions.GiveCash(amount, source, targetId) +end + +function NDCore.Functions.WithdrawMoney(amount, source) + local player = NDCore.getPlayer(source) + return player and player.withdrawMoney(amount) +end + +function NDCore.Functions.DepositMoney(amount, source) + local player = NDCore.getPlayer(source) + return player and player.depositMoney(amount) +end + +function NDCore.Functions.DeductMoney(amount, source, account, description) + local player = NDCore.getPlayer(source) + return player and player.deductMoney(amount, account, description) +end + +function NDCore.Functions.AddMoney(amount, source, account, description) + local player = NDCore.getPlayer(source) + return player and player.addMoney(amount, account, description) +end + +function NDCore.Functions.CreateCharacter(src, firstName, lastName, dob, gender, cb) + local player = NDCore.newCharacter(src, { + firstname = firstName, + lastname = lastName, + dob = dob, + gender = gender, + }) + + if cb then cb(player.id) end + player.triggerEvent("ND:returnCharacters", NDCore.fetchAllCharacters(src)) + return player.id +end + +function NDCore.Functions.UpdateCharacter(characterId, firstName, lastName, dob, gender) + local player = NDCore.fetchCharacter(characterId) + player.setData({ + source = src, + firstname = firstName, + lastname = lastName, + dob = dob, + gender = gender + }) + return player +end + +function NDCore.Functions.DeleteCharacter(characterId) + local player = NDCore.fetchCharacter(characterId) + return player and player.delete() +end + +function NDCore.Functions.SetPlayerData(characterId, key, value) + local player = NDCore.fetchCharacter(characterId) + if not player then return end + + if player[key] then + return player.setData(key, value) + end + return player.setMetadata(key, value) +end + +function NDCore.Functions.CreatePlayerLicense(characterId, licenseType, expire) + local player = NDCore.fetchCharacter(characterId) + return self.createLicense(licenseType, expire) +end + +-- find a players license by it's identifier. +function NDCore.Functions.FindLicenseByIdentifier(licences, identifier) + for key, license in pairs(licences) do + if license.identifier == identifier then + return license, key + end + end + return {} +end + +-- Edit a license by the license identifier. +function NDCore.Functions.EditPlayerLicense(characterId, identifier, newData) + local player = NDCore.fetchCharacter(characterId) + if not player then return end + + local licenses = player.metadata.licenses + if licenses then + local license, key = NDCore.Functions.FindLicenseByIdentifier(licenses, identifier) + for k, v in pairs(newData) do + license[k] = v + end + licenses[key] = license + player.setMetadata("licenses", licenses) + end + return licenses or {} +end + +-- Set the players job and job rank. +function NDCore.Functions.SetPlayerJob(characterId, job, rank) + local player = NDCore.fetchCharacter(characterId) + if not player then return end + if player.source then + local oldJob = player.getJob(job) + TriggerEvent("ND:jobChanged", player.source, {name = job, rank = rank or 1}, {name = player.job, rank = oldJob and oldJob.rank or 1}) + TriggerClientEvent("ND:jobChanged", player.source, {name = job, rank = rank or 1}, {name = player.job, rank = oldJob and oldJob.rank or 1}) + end + return player.setJob(job, rank) +end + +-- Set a player to a group. +function NDCore.Functions.SetPlayerToGroup(characterId, group, rank) + local player = NDCore.fetchCharacter(characterId) + return player and player.addGroup(group, rank) +end + +-- Remove a player from a group. +function NDCore.Functions.RemovePlayerFromGroup(characterId, group) + local player = NDCore.fetchCharacter(characterId) + return player and player.removeGroup(group) +end + +-- Update the characters last location into the database. +function NDCore.Functions.UpdateLastLocation(characterId, location) + local player = NDCore.fetchCharacter(characterId) + return player and player.setMetadata("location", { + x = location.x, + y = location.y, + x = location.z, + w = location.h or location.heading or location.w or 0.0 + }) +end + +-- Getting all the characters the player has and returning them to the client. +RegisterNetEvent("ND:GetCharacters", function() + local src = source + TriggerClientEvent("ND:returnCharacters", src, NDCore.fetchAllCharacters(src)) +end) + +-- Creating a new character. +RegisterNetEvent("ND:newCharacter", function(newCharacter) + local src = source + NDCore.newCharacter(src, { + firstname = newCharacter.firstName, + lastname = newCharacter.lastName, + dob = newCharacter.dob, + gender = newCharacter.gender, + cash = newCharacter.cash, + bank = newCharacter.bank, + }) +end) + +-- Update the character info when edited. +RegisterNetEvent("ND:editCharacter", function(newCharacter) + local src = source + local player = NDCore.fetchCharacter(newCharacter.id, src) + return player and player.setData({ + source = src, + firstname = newCharacter.firstName, + lastname = newCharacter.lastName, + dob = newCharacter.dob, + gender = newCharacter.gender + }) +end) + +-- Delete character from database. +RegisterNetEvent("ND:deleteCharacter", function(characterId) + local src = source + local player = NDCore.fetchCharacter(characterId, src) + if not player or player.source ~= source then return end + return player.delete() +end) + +-- add a player to the table. +RegisterNetEvent("ND:setCharacterOnline", function(id) + local src = source + NDCore.setActiveCharacter(src, tonumber(id)) +end) + +-- Update the characters clothes. +RegisterNetEvent("ND:updateClothes", function(clothing) + local src = source + local player = NDCore.getPlayer(src) + player.setMetadata("clothing", clothing) +end) From d31be85fd8d9391bf6b17d40b3c771cba2d48c32 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:29:22 +0200 Subject: [PATCH 053/161] update: license functions --- compatibility/backwards/server.lua | 12 +----------- server/player.lua | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/compatibility/backwards/server.lua b/compatibility/backwards/server.lua index 492a71c..a6dc434 100644 --- a/compatibility/backwards/server.lua +++ b/compatibility/backwards/server.lua @@ -158,17 +158,7 @@ end function NDCore.Functions.EditPlayerLicense(characterId, identifier, newData) local player = NDCore.fetchCharacter(characterId) if not player then return end - - local licenses = player.metadata.licenses - if licenses then - local license, key = NDCore.Functions.FindLicenseByIdentifier(licenses, identifier) - for k, v in pairs(newData) do - license[k] = v - end - licenses[key] = license - player.setMetadata("licenses", licenses) - end - return licenses or {} + player.updateLicense(identifier, newData) end -- Set the players job and job rank. diff --git a/server/player.lua b/server/player.lua index eface4f..c44e86e 100644 --- a/server/player.lua +++ b/server/player.lua @@ -172,6 +172,25 @@ local function createCharacterTable(info) end self.triggerEvent("ND:updateCharacter", self) end + + function self.getLicense(identifier) + local licenses = self.metadata.licenses or {} + for i=1, #licenses do + local data = licenses[i] + if data.identifier == identifier then + return data, i + end + end + end + + function self.updateLicense(identifier, newData) + local data, i = self.getLicense(identifier) + if not data then return end + for k, v in pairs(newData) do + data[k] = v + end + self.save() + end ---@param coords vector3|vector4 ---@return boolean From 858e5e98a23ed6551ef1fb6c92cee2e2efa5e5ad Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:29:36 +0200 Subject: [PATCH 054/161] fix: save players on resource stop --- server/main.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/main.lua b/server/main.lua index 11885ae..8f818bd 100644 --- a/server/main.lua +++ b/server/main.lua @@ -108,6 +108,14 @@ AddEventHandler("playerDropped", function() PlayersInfo[src] = nil end) +AddEventHandler("onResourceStop", function(name) + if name ~= resourceName then return end + for src, player in pairs(NDCore.players) do + player.save() + Wait(10) + end +end) + SetTimeout(500, function() NDCore.loadSQL({ "database/characters.sql", From 0baadda7b056b4248c7d9314a6b43891253e68be Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:30:15 +0200 Subject: [PATCH 055/161] update: notification reosurces --- server/player.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/player.lua b/server/player.lua index c44e86e..c91aa89 100644 --- a/server/player.lua +++ b/server/player.lua @@ -216,7 +216,11 @@ local function createCharacterTable(info) function self.notify(...) if not self.source then return end - TriggerClientEvent("ox_lib:notify", self.source, ...) + if GetResourceState("ModernHUD") == "started" then + TriggerClientEvent("ModernHUD:notify", self.source, ...) + elseif GetResourceState("ox_lib") == "started" then + TriggerClientEvent("ox_lib:notify", self.source, ...) + end return true end From eed4855b63a0247fe24ce121aba9ce8c5fcf5e02 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:30:36 +0200 Subject: [PATCH 056/161] update: start compatibility --- fxmanifest.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index 28d155c..6ecebb9 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -14,7 +14,8 @@ client_scripts { "shared/functions.lua", "client/vehicle.lua", "client/functions.lua", - "client/events.lua" + "client/events.lua", + "compatibility/**/client.lua" } server_scripts { "@oxmysql/lib/MySQL.lua", @@ -22,7 +23,8 @@ server_scripts { "shared/functions.lua", "server/player.lua", "server/vehicle.lua", - "server/functions.lua" + "server/functions.lua", + "compatibility/**/server.lua" } file "init.lua" From 86c38aea69dc21616548934b5ad40f40bd49d7fd Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:31:02 +0200 Subject: [PATCH 057/161] feat: add more info to veh props --- client/vehicle.lua | 198 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 1 deletion(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 0cd26f0..ef335e5 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -1,3 +1,190 @@ +local vehicleColorNames = { + [0] = "Black", + [1] = "Black", + [2] = "Black", + [3] = "Silver", + [4] = "Silver", + [5] = "Silver", + [6] = "Gray", + [7] = "Silver", + [8] = "Silver", + [9] = "Silver", + [10] = "Metal", + [11] = "Grey", + [12] = "Black", + [13] = "Gray", + [14] = "Grey", + [15] = "Black", + [16] = "Black", + [17] = "Silver", + [18] = "Silver", + [19] = "Metal", + [20] = "Silver", + [21] = "Black", + [22] = "Graphite", + [23] = "Silver", + [24] = "Silver", + [25] = "Silver", + [26] = "Silver", + [27] = "Red", + [28] = "Red", + [29] = "Red", + [30] = "Red", + [31] = "Red", + [32] = "Red", + [33] = "Red", + [34] = "Red", + [35] = "Red", + [36] = "Orange", + [37] = "Gold", + [38] = "Orange", + [39] = "Red", + [40] = "Red", + [41] = "Orange", + [42] = "Yellow", + [43] = "Red", + [44] = "Red", + [45] = "Red", + [46] = "Red", + [47] = "Red", + [48] = "Red", + [49] = "Green", + [50] = "Green", + [51] = "Green", + [52] = "Green", + [53] = "Green", + [54] = "Green", + [55] = "Green", + [56] = "Green", + [57] = "Green", + [58] = "Green", + [59] = "Green", + [60] = "Green", + [61] = "Blue", + [62] = "Blue", + [63] = "Blue", + [64] = "Blue", + [65] = "Blue", + [66] = "Blue", + [67] = "Blue", + [68] = "Blue", + [69] = "Blue", + [70] = "Blue", + [71] = "Blue", + [72] = "Blue", + [73] = "Blue", + [74] = "Blue", + [75] = "Blue", + [76] = "Blue", + [77] = "Blue", + [78] = "Blue", + [79] = "Bblue", + [80] = "Blue", + [81] = "Blue", + [82] = "Blue", + [83] = "Blue", + [84] = "Blue", + [85] = "Blue", + [86] = "Blue", + [87] = "Blue", + [88] = "Yellow", + [89] = "Yellow", + [90] = "Bronze", + [91] = "Yellow", + [92] = "Lime", + [93] = "Champagne", + [94] = "Beige", + [95] = "Ivory", + [96] = "Brown", + [97] = "Brown", + [98] = "Brown", + [99] = "Beige", + [100] = "Brown", + [101] = "Brown", + [102] = "Beechwood", + [103] = "Beechwood", + [104] = "Orange", + [105] = "Sand", + [106] = "Sand", + [107] = "Cream", + [108] = "Brown", + [109] = "Brown", + [110] = "Brown", + [111] = "White", + [112] = "White", + [113] = "Beige", + [114] = "Brown", + [115] = "Brown", + [116] = "Beige", + [117] = "Steel", + [118] = "Steel", + [119] = "Aluminium", + [120] = "Chrome", + [121] = "White", + [122] = "White", + [123] = "Orange", + [124] = "Orange", + [125] = "Green", + [126] = "Yellow", + [127] = "Blue", + [128] = "Green", + [129] = "Brown", + [130] = "Orange", + [131] = "White", + [132] = "White", + [133] = "Green", + [134] = "White", + [135] = "Pink", + [136] = "pink", + [137] = "Pink", + [138] = "Orange", + [139] = "Green", + [140] = "Blue", + [141] = "Black", + [142] = "Black", + [143] = "Black", + [144] = "Green", + [145] = "Purple", + [146] = "Blue", + [147] = "Black", + [148] = "Purple", + [149] = "Purple", + [150] = "Red", + [151] = "Green", + [152] = "Green", + [153] = "Brown", + [154] = "Tan", + [155] = "Green", + [156] = "ALLOY", + [157] = "Blue", +} + +local vehicleClassNames = { + [0] = "Compact", + [1] = "Sedan", + [2] = "SUV", + [3] = "Coupe", + [4] = "Muscle", + [5] = "Sports Classic", + [6] = "Sport", + [7] = "Super", + [8] = "Motorcycle", + [9] = "Off-road", + [10] = "Industrial", + [11] = "Utility", + [12] = "Van", + [13] = "Cycle", + [14] = "Boat", + [15] = "Helicopter", + [16] = "Plane", + [17] = "Service", + [18] = "Emergency", + [19] = "Military", + [20] = "Commercial", + [21] = "Train", + [22] = "Open wheel" +} + local function getVehicleBlipSprite(entity) if not IsEntityAVehicle(entity) then return 148 -- circle blip @@ -125,7 +312,16 @@ AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, end) lib.callback.register("ND_Vehicles:getProps", function() - return lib.getVehicleProperties(GetVehiclePedIsIn(cache.ped)) + local veh = GetVehiclePedIsIn(cache.ped) + local props = lib.getVehicleProperties(veh) + local colorPrimary, colorSecondary = GetVehicleColours(veh) + props.colorNamePrimary = vehicleColorNames[colorPrimary] + props.colorNameSecondary = vehicleColorNames[colorSecondary] + props.colorName = props.colorNamePrimary == props.colorNameSecondary and props.colorNamePrimary or ("%s & %s"):format(props.colorNamePrimary, props.colorNameSecondary) + props.className = vehicleClassNames[GetVehicleClass(veh)] + props.makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model)) + props.modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) + return props end) lib.callback.register("ND_Vehicles:getNearbyVehicleById", function(vehId) From 9738fc236bcb3c4643f469187070db5b8ebed133 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 23 Aug 2023 03:36:35 +0200 Subject: [PATCH 058/161] update: future compatibility frameworks --- compatibility/esx/.gitkeep | 0 compatibility/qb/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 compatibility/esx/.gitkeep create mode 100644 compatibility/qb/.gitkeep diff --git a/compatibility/esx/.gitkeep b/compatibility/esx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/compatibility/qb/.gitkeep b/compatibility/qb/.gitkeep new file mode 100644 index 0000000..e69de29 From 46fc396924a68321c7625f6c08f080edab58a2e6 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 24 Aug 2023 01:00:54 +0200 Subject: [PATCH 059/161] update: compat for get admin and update ace on group --- compatibility/backwards/server.lua | 7 +++++++ server/player.lua | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/compatibility/backwards/server.lua b/compatibility/backwards/server.lua index a6dc434..529c467 100644 --- a/compatibility/backwards/server.lua +++ b/compatibility/backwards/server.lua @@ -196,6 +196,13 @@ function NDCore.Functions.UpdateLastLocation(characterId, location) }) end +function NDCore.Functions.IsPlayerAdmin(src) + local player = NDCore.getPlayer(src) + if player.groups["admin"] then + return true + end +end + -- Getting all the characters the player has and returning them to the client. RegisterNetEvent("ND:GetCharacters", function() local src = source diff --git a/server/player.lua b/server/player.lua index c91aa89..4251c45 100644 --- a/server/player.lua +++ b/server/player.lua @@ -111,6 +111,9 @@ local function createCharacterTable(info) -- Unload and save character function self.unload() if not NDCore.players[self.source] then return end + for name, _ in pairs(self.groups) do + ExecuteCommand(("remove_principal identifier.%s group.%s"):format(self.identifier, name)) + end local ped = GetPlayerPed(self.source) if ped then local coords = GetEntityCoords(ped) @@ -235,6 +238,9 @@ local function createCharacterTable(info) local char = NDCore.players[self.source] if char and char.id == self.id then return true end if char then char.unload() end + for name, _ in pairs(self.groups) do + ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.identifier, name)) + end NDCore.players[self.source] = self TriggerEvent("ND:characterLoaded", self) self.triggerEvent("ND:characterLoaded", self) From 105ac16560af110bbdeaaf469631699710398b39 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 25 Aug 2023 03:09:34 +0200 Subject: [PATCH 060/161] fix(server/vehicle): entity exists check --- server/vehicle.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 3fca885..63aaf00 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -340,10 +340,12 @@ else NDCore.giveVehicleKeys(veh, src, target) end, false) end + RegisterNetEvent("entityCreated", function(entity) - if GetEntityType(entity) ~= 2 then return end + if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end local state = Entity(entity).state if state.owner or math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end if state.locked ~= nil then return end state.locked = true end) + From 5ec7a9ac8345c7d23cb4b2682182f6f808118657 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 25 Aug 2023 03:11:55 +0200 Subject: [PATCH 061/161] feat(client/vehicle): veh control & keys for engine Added check for keys to start engine Disabled vehicle air control Added saving wheel steering angle --- client/vehicle.lua | 80 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index ef335e5..7576d3b 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -185,6 +185,15 @@ local vehicleClassNames = { [22] = "Open wheel" } +local vehicleClassNotDisableAirControl = { + [8] = true, --motorcycle + [13] = true, --bicycles + [14] = true, --boats + [15] = true, --helicopter + [16] = true, --plane + [19] = true --military +} + local function getVehicleBlipSprite(entity) if not IsEntityAVehicle(entity) then return 148 -- circle blip @@ -342,8 +351,9 @@ lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) return ("%s %s"):format(make, name) end) -AddEventHandler("onResourceStart", function(resourceName) - if resourceName ~= "ox_inventory" or not Config.useInventoryForKeys then return end +Config.ox_inventory = NDCore.isResourceStarted("ox_inventory", function(started) + Config.ox_inventory = started + if not Config.useInventoryForKeys then return end Wait(1000) exports.ox_inventory:displayMetadata({ vehPlate = "Plate", @@ -351,9 +361,65 @@ AddEventHandler("onResourceStart", function(resourceName) }) end) -if GetResourceState("ox_inventory") == "started" then - exports.ox_inventory:displayMetadata({ - vehPlate = "Plate", - vehModel = "Model" - }) +local inventoryKeyCheck = { + lastCheck = 0, + hasKey = false +} + +local function hasVehicleKeys(veh) + if Config.useInventoryForKeys then + local time = GetCloudTimeAsInt() + if inventoryKeyCheck.lastCheck and (time-inventoryKeyCheck.lastCheck) < 5 then + return inventoryKeyCheck.hasKey + end + + local metadata = { + vehPlate = GetVehicleNumberPlateText(veh) + } + local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0 + inventoryKeyCheck.lastCheck = time + inventoryKeyCheck.hasKey = hasKey + return hasKey + end + local state = Entity(veh).state + local keys = state.keys + local player = NDCore.getPlayer() + return player and keys[player.id] end + +CreateThread(function() + local inVehcile = false + local wait = 500 + local angle = 0.0 + while true do + Wait(wait) + local veh = GetVehiclePedIsIn(cache.ped) + local isDriver = GetPedInVehicleSeat(veh, -1) == cache.ped + + if veh ~= 0 and isDriver then + local steeringAngle = GetVehicleSteeringAngle(veh) + if steeringAngle > 10.0 or steeringAngle < -10.0 then + angle = steeringAngle + end + if GetIsTaskActive(cache.ped, 2) then + SetVehicleSteeringAngle(veh, angle) -- save wheels steering angle. + end + + if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(veh)] and (IsEntityInAir(veh) or IsEntityUpsidedown(veh)) then + wait = 0 + DisableControlAction(0, 59) -- disable vehicle air control. + DisableControlAction(0, 60) + elseif not GetIsVehicleEngineRunning(veh) and not hasVehicleKeys(veh) then + wait = 0 + DisableControlAction(0, 59) + if IsVehicleEngineStarting(veh) then + SetVehicleEngineOn(veh, false, true, true) -- don't turn on engine if no keys. + end + else + wait = 500 + end + elseif wait ~= 500 then + wait = 500 + end + end +end) From 97e175d4fd33ece34ae81af8e5100c3bde0de5de Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 25 Aug 2023 04:24:29 +0200 Subject: [PATCH 062/161] fix(client/vehicles): check for started inventory --- client/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 7576d3b..25cc975 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -353,7 +353,7 @@ end) Config.ox_inventory = NDCore.isResourceStarted("ox_inventory", function(started) Config.ox_inventory = started - if not Config.useInventoryForKeys then return end + if not started or not Config.useInventoryForKeys then return end Wait(1000) exports.ox_inventory:displayMetadata({ vehPlate = "Plate", From fc1ef7b7b3271a3212f2478d256bdefed0557854 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 25 Aug 2023 04:25:48 +0200 Subject: [PATCH 063/161] update(server/player): use lib acl wrapper --- server/player.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/player.lua b/server/player.lua index 4251c45..aa93343 100644 --- a/server/player.lua +++ b/server/player.lua @@ -112,7 +112,7 @@ local function createCharacterTable(info) function self.unload() if not NDCore.players[self.source] then return end for name, _ in pairs(self.groups) do - ExecuteCommand(("remove_principal identifier.%s group.%s"):format(self.identifier, name)) + lib.removePrincipal(self.source, ("group.%s"):format(name)) end local ped = GetPlayerPed(self.source) if ped then @@ -239,7 +239,7 @@ local function createCharacterTable(info) if char and char.id == self.id then return true end if char then char.unload() end for name, _ in pairs(self.groups) do - ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.identifier, name)) + lib.addPrincipal(self.source, ("group.%s"):format(name)) end NDCore.players[self.source] = self TriggerEvent("ND:characterLoaded", self) @@ -268,7 +268,7 @@ local function createCharacterTable(info) isJob = isJob } self.triggerEvent("ND:updateCharacter", self) - ExecuteCommand(("add_principal identifier.%s group.%s"):format(self.identifier, name)) + lib.addPrincipal(self.source, ("group.%s"):format(name)) return true end @@ -285,7 +285,7 @@ local function createCharacterTable(info) end self.groups[name] = nil self.triggerEvent("ND:updateCharacter", self) - ExecuteCommand(("remove_principal identifier.%s group.%s"):format(self.identifier, name)) + lib.removePrincipal(self.source, ("group.%s"):format(name)) end ---@param name string From 5a1c4ecb1a42348e88767c7756cfb25996cc95a2 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 25 Aug 2023 04:59:31 +0200 Subject: [PATCH 064/161] feat(server/player): get metadata and player data --- server/player.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/server/player.lua b/server/player.lua index aa93343..0240767 100644 --- a/server/player.lua +++ b/server/player.lua @@ -65,6 +65,26 @@ local function createCharacterTable(info) return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw") end + ---@param data string + ---@return any + function self.getData(data) + return self[data] + end + + ---@param metadata string|table + ---@return any + function self.getMetadata(metadata) + if type(metadata) ~= "table" then + return self.metadata[metadata] + end + local returnData = {} + for i=1, #metadata then + local data = metadata[i] + returnData[data] = self.metadata[data] + end + return returnData + end + ---@param key string|table ---@param value any function self.setData(key, value) From 83cacb7ba6e5727512c37c178416cc6aca215642 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 26 Aug 2023 01:37:15 +0200 Subject: [PATCH 065/161] update(server/player): job info & data --- server/player.lua | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/player.lua b/server/player.lua index 0240767..275e442 100644 --- a/server/player.lua +++ b/server/player.lua @@ -78,7 +78,7 @@ local function createCharacterTable(info) return self.metadata[metadata] end local returnData = {} - for i=1, #metadata then + for i=1, #metadata do local data = metadata[i] returnData[data] = self.metadata[data] end @@ -276,7 +276,6 @@ local function createCharacterTable(info) local groupInfo = Config.groups[name] if not groupInfo then return end if isJob then - self.job = name for _, group in pairs(self.groups) do group.isJob = nil end @@ -300,9 +299,6 @@ local function createCharacterTable(info) ---@param name string function self.removeGroup(name) - if self.job == "name" then - self.job = nil - end self.groups[name] = nil self.triggerEvent("ND:updateCharacter", self) lib.removePrincipal(self.source, ("group.%s"):format(name)) @@ -317,14 +313,20 @@ local function createCharacterTable(info) ---@param job string ---@return boolean - function self.getJob(job) + function self.getJob() for name, group in pairs(self.groups) do - if group.isJob and not job or group.isJob and name == job then - return group + if group.isJob then + return name, group end end end + local jobName, jobInfo = self.getJob() + if jobInfo then + self.job = jobName + self.jobInfo = jobInfo + end + return self end From 0208f22df5f5a7678d0d224aa984c5a872f3b3cf Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 26 Aug 2023 04:35:34 +0200 Subject: [PATCH 066/161] feat(server): setmoney command --- fxmanifest.lua | 3 ++- server/commands.lua | 63 +++++++++++++++++++++++++++++++++++++++++++++ server/player.lua | 6 ++--- 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 server/commands.lua diff --git a/fxmanifest.lua b/fxmanifest.lua index 6ecebb9..264e9f4 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -24,7 +24,8 @@ server_scripts { "server/player.lua", "server/vehicle.lua", "server/functions.lua", - "compatibility/**/server.lua" + "compatibility/**/server.lua", + "server/commands.lua" } file "init.lua" diff --git a/server/commands.lua b/server/commands.lua new file mode 100644 index 0000000..a0108c2 --- /dev/null +++ b/server/commands.lua @@ -0,0 +1,63 @@ +local moneyTypes = {"bank", "cash"} +local moneyActions = { + remove = function(player, account, amount) + player.deductMoney(account, amount, "Staff action") + return ("Removed $%d (%s) to %s"):format(amount, account, player.name), ("removed $%d from %s"):format(amount, account) + end, + add = function(player, account, amount) + player.addMoney(account, amount, "Staff action") + return ("Added $%d (%s) to %s"):format(amount, account, player.name), ("added $%d to %s"):format(amount, account) + end, + set = function(player, account, amount) + player.setData(account, amount, "Staff action") + return ("Set %s's (%s) to $%d"):format(player.name, account, amount), ("set %s to $%d"):format(account, amount) + end +} + +lib.addCommand("setmoney", { + help = "Admin command, manage player money.", + -- restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + }, + { + name = "action", + type = "string", + help = "remove/add/set" + }, + { + name = "type", + type = "string", + help = "bank/cash" + }, + { + name = "amount", + type = "number", + help = "bank/cash" + } + }, +}, function(source, args, raw) + local action = moneyActions[args.action] + local moneyType = args.type:lower() + if not action or not lib.table.contains(moneyTypes, moneyType) then return end + + local player = NDCore.getPlayer(args.target) + local staffMessage, userMessage = action(player, moneyType, args.amount) + + player.notify({ + title = "Staff action", + description = userMessage, + type = "inform", + duration = 10000 + }) + + if not source then return end + TriggerClientEvent("chat:addMessage", source, { + color = {50, 100, 235}, + multiline = true, + args = {"Staff action", staffMessage} + }) +end) diff --git a/server/player.lua b/server/player.lua index 275e442..7c080dc 100644 --- a/server/player.lua +++ b/server/player.lua @@ -87,18 +87,18 @@ local function createCharacterTable(info) ---@param key string|table ---@param value any - function self.setData(key, value) + function self.setData(key, value, reason) if type(key) == "table" then for k, v in pairs(key) do self[k] = v if k == "cash" or k == "bank" then - TriggerEvent("ND:moneyChange", self.source, k, v, "set") + TriggerEvent("ND:moneyChange", self.source, k, v, "set", reason) end end else self[key] = value if key == "cash" or key == "bank" then - TriggerEvent("ND:moneyChange", self.source, key, value, "set") + TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason) end end self.triggerEvent("ND:updateCharacter", self) From a75a307873a4f07dd8f458068e2ca5d770eae37c Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 26 Aug 2023 04:36:07 +0200 Subject: [PATCH 067/161] perf(client/vehicle): hasVehicleKeys --- client/vehicle.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 25cc975..32f4684 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -361,30 +361,35 @@ Config.ox_inventory = NDCore.isResourceStarted("ox_inventory", function(started) }) end) -local inventoryKeyCheck = { - lastCheck = 0, +local keyCheckTime = { + lastCheck = GetCloudTimeAsInt(), hasKey = false } local function hasVehicleKeys(veh) - if Config.useInventoryForKeys then - local time = GetCloudTimeAsInt() - if inventoryKeyCheck.lastCheck and (time-inventoryKeyCheck.lastCheck) < 5 then - return inventoryKeyCheck.hasKey - end + local time = GetCloudTimeAsInt() + if time-keyCheckTime.lastCheck < 5 then + return keyCheckTime.hasKey + end + if Config.ox_inventory and Config.useInventoryForKeys then local metadata = { vehPlate = GetVehicleNumberPlateText(veh) } local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0 - inventoryKeyCheck.lastCheck = time - inventoryKeyCheck.hasKey = hasKey + keyCheckTime.lastCheck = time + keyCheckTime.hasKey = hasKey return hasKey end + local state = Entity(veh).state local keys = state.keys local player = NDCore.getPlayer() - return player and keys[player.id] + local hasKey = player and keys and keys[player.id] + + keyCheckTime.lastCheck = time + keyCheckTime.hasKey = hasKey + return hasKey end CreateThread(function() From e4e5d60cf46f0c2dea96d84eca9b8aaeb596c394 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 26 Aug 2023 04:36:37 +0200 Subject: [PATCH 068/161] Update commands.lua --- server/commands.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/commands.lua b/server/commands.lua index a0108c2..12d583d 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -16,7 +16,7 @@ local moneyActions = { lib.addCommand("setmoney", { help = "Admin command, manage player money.", - -- restricted = "group.admin", + restricted = "group.admin", params = { { name = "target", From bde1f5b0bb8cb10be0acba2ec9e3a6178c3e130b Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:22:27 +0200 Subject: [PATCH 069/161] update(vehicle): lockpick event & alarm --- client/vehicle.lua | 5 +---- server/vehicle.lua | 9 ++++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 32f4684..1af87e9 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -243,15 +243,12 @@ RegisterNetEvent("ND_Vehicles:blip", function(netid, status) EndTextCommandSetBlipName(blip) end) -RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid, success, action) +RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid) local veh = NetToVeh(netid) if not veh then return end SetVehicleAlarmTimeLeft(veh, 1) SetVehicleAlarm(veh, true) StartVehicleAlarm(veh) - if success and action == "lockpick" then - setVehicleLocked(veh, false) - end end) RegisterNetEvent("ND_VehicleSystem:setOwnedIfNot", function(netid) diff --git a/server/vehicle.lua b/server/vehicle.lua index 63aaf00..cd735ea 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -231,10 +231,13 @@ function NDCore.saveVehicleProperties(source, veh, properties) end end -RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid, success, action) - local veh = NetworkGetEntityFromNetworkId(netid) +RegisterNetEvent("ND_Vehicles:lockpick", function(netId, success) + local veh = NetworkGetEntityFromNetworkId(netId) local owner = NetworkGetEntityOwner(veh) - TriggerClientEvent("ND_Vehicles:syncAlarm", owner, netid, success, action) + TriggerClientEvent("ND_Vehicles:syncAlarm", owner, netId) + if not success then return end + local state = Entity(veh).state + state.locked = false end) if Config.useInventoryForKeys then From 45fe62c7c5e629886fc17a4daa96c35434d6f15d Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:23:06 +0200 Subject: [PATCH 070/161] fix(client/vehicle): return vehicle entity --- client/vehicle.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 1af87e9..11248e0 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -335,9 +335,9 @@ lib.callback.register("ND_Vehicles:getNearbyVehicleById", function(vehId) local vehicles = lib.getNearbyVehicles(coords, 25.0, true) for i=1, #vehicles do local veh = vehicles[i] - local state = Entity(veh).state + local state = Entity(veh.vehicle).state if state and state.id == vehId then - return VehToNet(veh) + return VehToNet(veh.vehicle) end end end) From a381ad793fba9d220e3a1a782c41370813029931 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:26:13 +0200 Subject: [PATCH 071/161] feat(client/vehicle): key checking to start vehicle --- client/vehicle.lua | 136 ++++++++++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 50 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 11248e0..8020666 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -348,74 +348,110 @@ lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) return ("%s %s"):format(make, name) end) -Config.ox_inventory = NDCore.isResourceStarted("ox_inventory", function(started) +local playerVehicle = cache.seat == -1 and cache.vehicle +local cloudTime = GetCloudTimeAsInt() +local vehicleLockCheckTime = { + lastCheck = cloudTime, + lastUse = cloudTime +} +local keyCheckTime = { + lastCheck = cloudTime, + hasKey = false +} + +local function hasVehicleKeys(veh) + local state = Entity(veh).state + if Config.ox_inventory and Config.useInventoryForKeys then + local metadata = { + vehPlate = GetVehicleNumberPlateText(veh), + keyEnabled = true + } + local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0 + return hasKey or state.hotwired + end + + local keys = state and state.keys + local player = NDCore.getPlayer() + local hasKey = player and keys and keys[player.id] + return hasKey or state.hotwired +end + +local function hasVehicleKeysCheck(veh) + local time = GetCloudTimeAsInt() + if time-keyCheckTime.lastCheck < 5 then + return keyCheckTime.hasKey + end + + local hasKey = hasVehicleKeys(veh) + keyCheckTime.lastCheck = time + keyCheckTime.hasKey = hasKey + return hasKey +end + +local function getNearestVehicle(hasKeysForOnly) + local coords = GetEntityCoords(cache.ped) + local vehicles = lib.getNearbyVehicles(coords, 25.0, true) + local nearestVeh = {} + + local function setNearestVehicle(veh) + if hasKeysForOnly and not hasVehicleKeys(veh.vehicle) then return end + local nearestDist = nearestVeh.dist + local dist = #(GetEntityCoords(cache.ped)-veh.coords) + if not nearestDist or dist < nearestDist then + nearestVeh.dist = dist + nearestVeh.coords = veh.coords + nearestVeh.entity = veh.vehicle + end + end + + for i=1, #vehicles do + setNearestVehicle(vehicles[i]) + end + return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist +end + +NDCore.isResourceStarted("ox_inventory", function(started) Config.ox_inventory = started - if not started or not Config.useInventoryForKeys then return end + if not started or not Config.useInventoryForKeys then + return vehicleLockKeybind:disable(false) + end Wait(1000) + vehicleLockKeybind:disable(true) exports.ox_inventory:displayMetadata({ vehPlate = "Plate", vehModel = "Model" }) end) -local keyCheckTime = { - lastCheck = GetCloudTimeAsInt(), - hasKey = false -} - -local function hasVehicleKeys(veh) - local time = GetCloudTimeAsInt() - if time-keyCheckTime.lastCheck < 5 then - return keyCheckTime.hasKey - end - - if Config.ox_inventory and Config.useInventoryForKeys then - local metadata = { - vehPlate = GetVehicleNumberPlateText(veh) - } - local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0 - keyCheckTime.lastCheck = time - keyCheckTime.hasKey = hasKey - return hasKey - end - - local state = Entity(veh).state - local keys = state.keys - local player = NDCore.getPlayer() - local hasKey = player and keys and keys[player.id] - - keyCheckTime.lastCheck = time - keyCheckTime.hasKey = hasKey - return hasKey -end - +-- save wheels steering angle. CreateThread(function() - local inVehcile = false - local wait = 500 local angle = 0.0 while true do - Wait(wait) - local veh = GetVehiclePedIsIn(cache.ped) - local isDriver = GetPedInVehicleSeat(veh, -1) == cache.ped - - if veh ~= 0 and isDriver then - local steeringAngle = GetVehicleSteeringAngle(veh) - if steeringAngle > 10.0 or steeringAngle < -10.0 then - angle = steeringAngle - end + Wait(300) + if playerVehicle then if GetIsTaskActive(cache.ped, 2) then - SetVehicleSteeringAngle(veh, angle) -- save wheels steering angle. + SetVehicleSteeringAngle(playerVehicle, angle) end - - if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(veh)] and (IsEntityInAir(veh) or IsEntityUpsidedown(veh)) then + angle = DoesEntityExist(playerVehicle) and GetVehicleSteeringAngle(playerVehicle) + end + end +end) + +CreateThread(function() + local wait = 500 + while true do + Wait(wait) + playerVehicle = cache.seat == -1 and cache.vehicle + if playerVehicle then + if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(playerVehicle)] and (IsEntityInAir(playerVehicle) or IsEntityUpsidedown(playerVehicle)) then wait = 0 DisableControlAction(0, 59) -- disable vehicle air control. DisableControlAction(0, 60) - elseif not GetIsVehicleEngineRunning(veh) and not hasVehicleKeys(veh) then + elseif not GetIsVehicleEngineRunning(playerVehicle) and not hasVehicleKeysCheck(playerVehicle) then wait = 0 DisableControlAction(0, 59) - if IsVehicleEngineStarting(veh) then - SetVehicleEngineOn(veh, false, true, true) -- don't turn on engine if no keys. + if DoesEntityExist(playerVehicle) and IsVehicleEngineStarting(playerVehicle) then + SetVehicleEngineOn(playerVehicle, false, true, true) -- don't turn on engine if no keys. end else wait = 500 From 57d6bbf85ae4601206e7bb4815e133b410d1a09f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:26:43 +0200 Subject: [PATCH 072/161] feat(client/vehicle): lock keybind for non inventory use --- client/vehicle.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 8020666..afe3fd2 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -410,6 +410,22 @@ local function getNearestVehicle(hasKeysForOnly) return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist end +local vehicleLockKeybind = lib.addKeybind({ + name = "vehicleKey", + description = "Unlock/lock vehicle (double click)", + defaultKey = "E", + onPressed = function(self) + local time = GetCloudTimeAsInt() + if time-vehicleLockCheckTime.lastCheck < 1 and time-vehicleLockCheckTime.lastUse > 1 then + vehicleLockCheckTime.lastUse = time + local veh = getNearestVehicle(true) + if not veh then return end + TriggerServerEvent("ND_Vehicles:toggleVehicleLock", VehToNet(veh)) + end + vehicleLockCheckTime.lastCheck = time + end +}) + NDCore.isResourceStarted("ox_inventory", function(started) Config.ox_inventory = started if not started or not Config.useInventoryForKeys then From 74b35442c591bbe26e2eb9ca13836054a3b44a83 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:28:29 +0200 Subject: [PATCH 073/161] feat(vehicle): lockpick & hotwire --- client/vehicle.lua | 115 +++++++++++++++++++++++++++++++++++++++++++++ server/vehicle.lua | 9 ++++ 2 files changed, 124 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index afe3fd2..b5ac511 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -477,3 +477,118 @@ CreateThread(function() end end end) + +local function hotwireVehicle() + local state = playerVehicle and Entity(playerVehicle).state + if not playerVehicle or state.hotwired then return end + + local finished = false + lib.requestModel(`imp_prop_impexp_pliers_02`) + lib.requestModel(`prop_tool_screwdvr01`) + lib.requestAnimDict("veh@handler@base") + + if GetFollowVehicleCamViewMode() > 2 then + SetFollowVehicleCamViewMode(0) + end + + CreateThread(function() + while not finished do + Wait(0) + DisableFirstPersonCamThisFrame() + end + end) + + local modelWithHandles = {`seashark`, `seashark2`, `seashark3`} + local vehicleModel = GetEntityModel(playerVehicle) + local bikeHandles = GetVehicleClass(playerVehicle) == 8 or lib.table.contains(modelWithHandles, vehicleModel) + + local success = lib.progressCircle({ + duration = math.random(10000, 20000), + label = "Hotwiring", + useWhileDead = false, + allowRagdoll = false, + allowCuffed = false, + allowFalling = false, + canCancel = true, + anim = { + dict = bikeHandles and "anim@veh@boat@jetski@front@base" or "veh@handler@base", + clip = "hotwire" + }, + disable = { + move = true, + car = true, + combat = true + }, + prop = { + { + model = `imp_prop_impexp_pliers_02`, + bone = 0x49D9, -- SKEL_R_Hand + pos = vec3(0.1, -0.05, 0.0), + rot = vec3(-1.5, -15.0, -1.5) + }, + { + model = `prop_tool_screwdvr01`, + bone = 0xDEAD, -- SKEL_R_Hand + pos = vec3(0.1, 0.08, -0.03), + rot = vec3(90.0, 0.0, 0.0) + } + } + }) + + finished = true + if not success then return end + if not playerVehicle then return false, true end + TriggerServerEvent("ND_Vehicles:hotwire", VehToNet(playerVehicle)) + return true, true +end + +local function lockpickVehicle() + if cache.vehicle then return end + local pos = GetEntityCoords(cache.ped) + local rot = GetEntityRotation(cache.ped, 2) + local veh = lib.getClosestVehicle(pos, 2.5, false) + if not veh then return end + + local dificulties = { + "easy", + "medium", + "hard" + } + local dificultyTime = { + easy = 500, + medium = 800, + hard = 1000 + } + + lib.requestAnimDict("veh@break_in@0h@p_m_one@") + for i=1, 7 do + TaskPlayAnimAdvanced(cache.ped, "veh@break_in@0h@p_m_one@", "std_force_entry_ds", pos.x, pos.y, pos.z+0.025, rot.x, rot.y, rot.z, 8.0, 8.0, 1800, 28, 0.1) + local dificulty = dificulties[math.random(1, #dificulties)] + local success = lib.skillCheck(dificulty) + if not success or not DoesEntityExist(veh) or #(pos-GetEntityCoords(veh)) > 2.5 then + TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), false) + return false, true + end + Wait(dificultyTime[dificulty]) + end + + veh = lib.getClosestVehicle(pos, 2.5, false) + if not veh then return false, true end + TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), true) + PlaySoundFromEntity(-1, "Remote_Control_Fob", cache.ped, "PI_Menu_Sounds", true, 0) + return true, true +end + +exports("lockpick", function(data, slot) + local _, used = lockpickVehicle() + if used then + exports.ox_inventory:useItem(data) + end +end) + +exports("hotwire", function(data, slot) + local _, used = hotwireVehicle() + if used then + exports.ox_inventory:useItem(data) + end +end) diff --git a/server/vehicle.lua b/server/vehicle.lua index cd735ea..1269afe 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -268,6 +268,15 @@ if Config.useInventoryForKeys then return end player.notify({ +RegisterNetEvent("ND_Vehicles:hotwire", function(netId) + local src = source + local ped = GetPlayerPed(src) + local playerVeh = GetVehiclePedIsIn(ped) + local veh = NetworkGetEntityFromNetworkId(netId) + if not playerVeh or playerVeh == 0 or playerVeh ~= veh then return end + local state = Entity(veh).state + state.hotwired = true +end) title = "No signal", description = "Vehicle to far away.", type = "error", From 5ef6afca6894a1a3b0c5554c8849cc4af818394f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:29:34 +0200 Subject: [PATCH 074/161] refactor(server/vehicle): using keys & inventory --- server/vehicle.lua | 242 +++++++++++++++++++++++++-------------------- 1 file changed, 136 insertions(+), 106 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 1269afe..8ad8212 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -1,5 +1,13 @@ +local ox_inventory +local inventoryStarted = false local playerOwnedVehicles = {} +NDCore.isResourceStarted("ox_inventory", function(started) + inventoryStarted = started + if not started then return end + ox_inventory = exports.ox_inventory +end) + local function isPlateAvailable(plate) return not MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) end @@ -179,21 +187,20 @@ function NDCore.spawnOwnedVehicle(source, vehicleID, coords, heading) state.props = vehicle.properties state.locked = true - if GetResourceState("ox_inventory") == "started" and Config.useInventoryForKeys then - if exports.ox_inventory:GetItem(source, "keys", {vehId = vehicle.id}, true) == 0 then - exports.ox_inventory:AddItem(source, "keys", 1, { - vehOwner = vehicle.owner, - vehId = vehicle.id, - vehPlate = vehicle.properties and vehicle.properties.plate, - vehModel = vehicle.properties and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, vehicle.properties.model) or "" - }) - end - else - state.keys = { - [player.id] = true - } + if inventoryStarted and Config.useInventoryForKeys and ox_inventory:GetItem(source, "keys", {vehId = vehicle.id}, true) == 0 then + ox_inventory:AddItem(source, "keys", 1, { + vehOwner = vehicle.owner, + vehId = vehicle.id, + vehPlate = vehicle.properties and vehicle.properties.plate, + vehModel = vehicle.properties and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, vehicle.properties.model) or "", + keyEnabled = true + }) end + state.keys = { + [player.id] = true + } + return true end end @@ -240,34 +247,6 @@ RegisterNetEvent("ND_Vehicles:lockpick", function(netId, success) state.locked = false end) -if Config.useInventoryForKeys then - local function toggleVehicleLock(id, veh, nearby) - local player = NDCore.getPlayer(id) - if nearby then - local state = Entity(veh).state - local locked = not state.locked - state.locked = locked - player.triggerEvent("ND_Vehicles:keyFob", NetworkGetNetworkIdFromEntity(veh)) - if locked then - player.notify({ - title = "LOCKED", - description = "Your vehicle has now been locked.", - type = "success", - position = "bottom-right", - duration = 3000 - }) - return - end - player.notify({ - title = "UNLOCKED", - description = "Your vehicle has now been unlocked.", - type = "inform", - position = "bottom-right", - duration = 3000 - }) - return - end - player.notify({ RegisterNetEvent("ND_Vehicles:hotwire", function(netId) local src = source local ped = GetPlayerPed(src) @@ -277,6 +256,20 @@ RegisterNetEvent("ND_Vehicles:hotwire", function(netId) local state = Entity(veh).state state.hotwired = true end) + +local function toggleVehicleLock(source, veh, nearby, metadata) + local player = NDCore.getPlayer(source) + + if metadata and not metadata.keyEnabled then + return player.notify({ + title = "No signal", + description = "Vehicle key disabled.", + type = "error", + position = "bottom-right", + duration = 3000 + }) + elseif not nearby then + return player.notify({ title = "No signal", description = "Vehicle to far away.", type = "error", @@ -285,74 +278,111 @@ end) }) end - - exports("keys", function(event, item, inventory, slot, data) - if event ~= "usingItem" then return end - local metadata - for i=1, #inventory.items do - local item = inventory.items[i] - if item.slot == slot then - metadata = item.metadata - break - end - end - - if not metadata then return false end - local veh = playerOwnedVehicles[metadata.vehId] and playerOwnedVehicles[metadata.vehId].entity - if veh and DoesEntityExist(veh) then - local ped = GetPlayerPed(inventory.id) - local pedCoords = GetEntityCoords(ped) - local vehCoords = GetEntityCoords(veh) - if not pedCoords or not vehCoords then return end - toggleVehicleLock(inventory.id, veh, #(pedCoords-vehCoords) < 25.0) - return false - end - - lib.callback("ND_Vehicles:getNearbyVehicleById", inventory.id, function(netId) - if not netId then return end - local veh = NetworkGetEntityFromNetworkId(netId) - if veh then - toggleVehicleLock(inventory.id, veh, true) - end - end, metadata.vehId) - return false - end) - - RegisterCommand("getkeys", function(source, args, rawCommand) - if GetResourceState("ox_inventory") ~= "started" then return end - local veh = GetVehiclePedIsIn(GetPlayerPed(source)) - if not veh or veh == 0 then return end - - local player = NDCore.getPlayer(source) - local state = Entity(veh).state - local owner = state.owner - if not owner or owner ~= player.id then return end - - local props = state.props - exports.ox_inventory:AddItem(source, "keys", 1, { - vehOwner = owner, - vehId = state.id, - vehPlate = props.plate, - vehModel = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, props.model) + local state = Entity(veh).state + local locked = not state.locked + state.locked = locked + player.triggerEvent("ND_Vehicles:keyFob", NetworkGetNetworkIdFromEntity(veh)) + if locked then + return player.notify({ + title = "LOCKED", + description = "Your vehicle has now been locked.", + type = "success", + position = "bottom-right", + duration = 3000 }) - end, false) -else - RegisterCommand("givekeys", function(source, args, rawCommand) - local src = source - if not args[1] then return end - local target = tonumber(args[1]) - if not GetPlayerPing(target) then return end - - local veh = GetVehiclePedIsIn(GetPlayerPed(src)) - if veh == 0 then - veh = GetVehiclePedIsIn(GetPlayerPed(src), true) - if veh == 0 then return end - end - - NDCore.giveVehicleKeys(veh, src, target) - end, false) + end + player.notify({ + title = "UNLOCKED", + description = "Your vehicle has now been unlocked.", + type = "inform", + position = "bottom-right", + duration = 3000 + }) end +local function lockNearestVehicle(source, vehId, metadata) + local veh = playerOwnedVehicles[vehId] and playerOwnedVehicles[vehId].entity + if veh and DoesEntityExist(veh) then + local ped = GetPlayerPed(source) + local pedCoords = GetEntityCoords(ped) + local vehCoords = GetEntityCoords(veh) + if not pedCoords or not vehCoords then return end + return toggleVehicleLock(source, veh, #(pedCoords-vehCoords) < 25.0, metadata) + end + + lib.callback("ND_Vehicles:getNearbyVehicleById", source, function(netId) + local veh = netId and NetworkGetEntityFromNetworkId(netId) + if not veh then return end + toggleVehicleLock(source, veh, true, metadata) + end, vehId) +end + +exports("keys", function(event, item, inventory, slot, data) + if event ~= "usingItem" or not Config.useInventoryForKeys or not inventoryStarted then return end + local metadata + for i=1, #inventory.items do + local item = inventory.items[i] + if item.slot == slot then + metadata = item.metadata + break + end + + if not metadata then return false end + lockNearestVehicle(inventory.id, metadata.vehId, metadata) + return false +end) + +RegisterCommand("getkeys", function(source, args, rawCommand) + if not Config.useInventoryForKeys or not inventoryStarted then return end + local veh = GetVehiclePedIsIn(GetPlayerPed(source)) + if not veh or veh == 0 then return end + + local player = NDCore.getPlayer(source) + local state = Entity(veh).state + local owner = state.owner + if not owner or owner ~= player.id then return end + + local props = state.props + ox_inventory:AddItem(source, "keys", 1, { + vehOwner = owner, + vehId = state.id, + vehPlate = props.plate, + vehModel = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, props.model), + keyEnabled = true + }) +end, false) + +RegisterCommand("givekeys", function(source, args, rawCommand) + if Config.useInventoryForKeys and inventoryStarted then return end + + local src = source + if not args[1] then return end + local target = tonumber(args[1]) + if not GetPlayerPing(target) then return end + + local veh = GetVehiclePedIsIn(GetPlayerPed(src)) + if veh == 0 then + veh = GetVehiclePedIsIn(GetPlayerPed(src), true) + if veh == 0 then return end + end + + NDCore.giveVehicleKeys(veh, src, target) +end, false) + +RegisterNetEvent("ND_Vehicles:toggleVehicleLock", function(netId) + if Config.useInventoryForKeys and inventoryStarted then return end + + local src = source + local veh = NetworkGetEntityFromNetworkId(netId) + if not DoesEntityExist(veh) then return end + + local ped = GetPlayerPed(src) + local pedCoords = GetEntityCoords(ped) + local vehCoords = GetEntityCoords(veh) + if not pedCoords or not vehCoords then return end + toggleVehicleLock(src, veh, #(pedCoords-vehCoords) < 25.0) +end) + RegisterNetEvent("entityCreated", function(entity) if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end local state = Entity(entity).state From f586bd17fbb885106c21287ea53f2e04d624ad00 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:31:08 +0200 Subject: [PATCH 075/161] feat(client/vehicle): key control (trunk) --- client/vehicle.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index b5ac511..334e3bb 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -592,3 +592,31 @@ exports("hotwire", function(data, slot) exports.ox_inventory:useItem(data) end end) +exports("keyControl", function(action, slot) + for item, data in pairs(exports.ox_inventory:Items()) do + local metadata = data.metadata + if data.slot == slot then + if metadata and not metadata.keyEnabled then + return lib.notify({ + title = "No signal", + description = "Vehicle key disabled.", + type = "error", + position = "bottom-right", + duration = 3000 + }) + end + break + end + end + if action == "trunk" then + local veh = getNearestVehicle(hasKeysForOnly) + if not veh then return end + exports.ox_inventory:closeInventory() + playKeyFob(veh) + if GetVehicleDoorAngleRatio(veh, 5) > 0.0 then + SetVehicleDoorShut(veh, 5) + else + SetVehicleDoorOpen(veh, 5, false) + end + end +end) From 0d64a1dfec96fc1a9fa4bf070b72aac49b4bcc76 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:31:29 +0200 Subject: [PATCH 076/161] feat(server/vehicle): disable key in inventory --- client/vehicle.lua | 14 ++++++++++++++ server/vehicle.lua | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 334e3bb..7306dd1 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -592,6 +592,7 @@ exports("hotwire", function(data, slot) exports.ox_inventory:useItem(data) end end) + exports("keyControl", function(action, slot) for item, data in pairs(exports.ox_inventory:Items()) do local metadata = data.metadata @@ -618,5 +619,18 @@ exports("keyControl", function(action, slot) else SetVehicleDoorOpen(veh, 5, false) end + elseif action == "disable" then + TriggerServerEvent("ND_Vehicles:disableKey", slot) + exports.ox_inventory:closeInventory() + lib.notify({ + title = "Key disabled", + description = "This vehicle key has been disabled and cannot be used anymore.", + type = "inform", + position = "bottom-right", + duration = 3000 + }) + end +end) + end end) diff --git a/server/vehicle.lua b/server/vehicle.lua index 8ad8212..c2b2152 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -326,6 +326,7 @@ exports("keys", function(event, item, inventory, slot, data) metadata = item.metadata break end + end if not metadata then return false end lockNearestVehicle(inventory.id, metadata.vehId, metadata) @@ -391,3 +392,12 @@ RegisterNetEvent("entityCreated", function(entity) state.locked = true end) +RegisterNetEvent("ND_Vehicles:disableKey", function(slot) + local src = source + local key = ox_inventory:GetSlot(src, slot) + local metadata = key.metadata + if not metadata.keyEnabled then return end + metadata.keyEnabled = false + ox_inventory:SetMetadata(src, slot, key.metadata) +end) + From 1e6b0f4c490cd9666512cd45d40d9123dfa0949a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:31:54 +0200 Subject: [PATCH 077/161] feat(client/vehicle): cruise control keybind --- client/vehicle.lua | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 7306dd1..6293d64 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -632,5 +632,79 @@ exports("keyControl", function(action, slot) end end) +local cruiseSpeedSet = 0 +local cruiseSpeedVehicle = 0 +local cruiseControlEnabled = false + +local function cruiseControl() + cruiseSpeedVehicle = GetEntitySpeed(playerVehicle) * 2.236936 + if not playerVehicle then + lib.notify({ + title = "Cruise control", + description = "Vehicle cruise control disabled.", + type = "inform", + position = "bottom-right", + duration = 3000 + }) + return + end + if cruiseSpeedVehicle < cruiseSpeedSet/3 then + lib.notify({ + title = "Cruise control", + description = "Vehicle cruise control disabled.", + type = "inform", + position = "bottom-right", + duration = 3000 + }) + return + end + if cruiseSpeedVehicle < cruiseSpeedSet then + SetControlNormal(0, 71, 0.6) + end + return true +end + +lib.addKeybind({ + name = "vehicleCruiseControl", + description = "Toggle vehicle cruise control", + defaultKey = "", + onPressed = function(self) + if cruiseControlEnabled and cruiseSpeedVehicle-1 > cruiseSpeedSet then + cruiseSpeedSet = cruiseSpeedVehicle + return lib.notify({ + title = "Cruise control", + description = ("Increased to %d mph."):format(math.floor(cruiseSpeedSet)), + type = "inform", + position = "bottom-right", + duration = 3000 + }) + elseif cruiseControlEnabled then + cruiseControlEnabled = false + return lib.notify({ + title = "Cruise control", + description = "Vehicle cruise control disabled.", + type = "inform", + position = "bottom-right", + duration = 3000 + }) + end + if not playerVehicle then return end + cruiseControlEnabled = true + cruiseSpeedVehicle = math.floor(GetEntitySpeed(playerVehicle) * 2.236936) + cruiseSpeedSet = cruiseSpeedVehicle + lib.notify({ + title = "Cruise control", + description = ("Set to %d mph."):format(math.floor(cruiseSpeedSet)), + type = "inform", + position = "bottom-right", + duration = 3000 + }) + CreateThread(function() + while cruiseControlEnabled and cruiseControl() do Wait(0) end + cruiseControlEnabled = false + end) + end +}) + end end) From 32801873f6a40f804896beebeb2bbe19e69c93f6 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 01:32:20 +0200 Subject: [PATCH 078/161] feat(client/server): change veh seat keybind --- client/vehicle.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 6293d64..8cf71c1 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -706,5 +706,30 @@ lib.addKeybind({ end }) +lib.addKeybind({ + name = "vehicleShuffleSeat", + description = "Suffle vehicle seat.", + defaultKey = "", + onPressed = function(self) + if not cache.vehicle then return end + local seats = { + [-1] = 0, + [0] = -1, + [1] = 2, + [2] = 1, + [3] = 4, + [4] = 3, + [5] = 6, + [6] = 5 + } + SetPedIntoVehicle(cache.ped, cache.vehicle, seats[cache.seat]) end +}) + +lib.onCache("ped", function(value) + SetPedConfigFlag(value, 184, true) +end) + +SetTimeout(500, function() + SetPedConfigFlag(cache.ped, 184, true) end) From 0ded085a28655f2717c3d6be77a84d2e29299d1a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 27 Aug 2023 17:59:22 +0200 Subject: [PATCH 079/161] tweak(server): character saving and unloading --- server/main.lua | 8 +++----- server/player.lua | 9 ++++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/server/main.lua b/server/main.lua index 8f818bd..c448621 100644 --- a/server/main.lua +++ b/server/main.lua @@ -102,16 +102,14 @@ end) AddEventHandler("playerDropped", function() local src = source local char = NDCore.players[src] - if char then - char.unload() - end + if char then char.unload() end PlayersInfo[src] = nil end) AddEventHandler("onResourceStop", function(name) if name ~= resourceName then return end - for src, player in pairs(NDCore.players) do - player.save() + for _, player in pairs(NDCore.players) do + player.unload() Wait(10) end end) diff --git a/server/player.lua b/server/player.lua index 7c080dc..c92a32c 100644 --- a/server/player.lua +++ b/server/player.lua @@ -145,14 +145,16 @@ local function createCharacterTable(info) w = heading }) end + self.triggerEvent("ND:characterUnloaded") TriggerEvent("ND:characterUnloaded", self.source, self) - self.save() + local saved = self.save() NDCore.players[self.source] = nil + return saved end -- Save character information to database function self.save() - MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ?, inventory = ? WHERE charid = ?", { + local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ?, inventory = ? WHERE charid = ?", { self.name, self.firstname, self.lastname, @@ -163,8 +165,9 @@ local function createCharacterTable(info) json.encode(self.groups), json.encode(self.metadata), json.encode(self.inventory), - self.id, + self.id }) + return affectedRows > 0 end ---Create a license/permit for the character From dff0be470209d9040b49c0b501ef9ba17587fa1f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 01:36:22 +0200 Subject: [PATCH 080/161] tweak(server/vehicle): don't lock veh with player driver --- server/vehicle.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index c2b2152..f9ea97b 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -387,8 +387,15 @@ end) RegisterNetEvent("entityCreated", function(entity) if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end local state = Entity(entity).state - if state.owner or math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end - if state.locked ~= nil then return end + if state.owner or state.locked ~= nil then return end + + local driver = GetPedInVehicleSeat(entity, -1) + if DoesEntityExist(driver) and IsPedAPlayer(driver) then + state.locked = false + state.hotwired = true + end + + if math.random(1, 100) <= Config.randomUnlockedVehicleChance then return end state.locked = true end) From 4868eb42a20cad6955773cddf44a27313b3819c6 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 01:37:22 +0200 Subject: [PATCH 081/161] refactor(server/vehicle): vehicleID to vehicleId --- server/vehicle.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index f9ea97b..2737f42 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -44,13 +44,13 @@ local function getVehicles(characterId) return vehicles end -function NDCore.transferVehicle(vehicleID, fromSource, toSource) +function NDCore.transferVehicle(vehicleId, fromSource, toSource) local playerTo = NDCore.getPlayer(toSource) local playerFrom = NDCore.getPlayer(fromSource) - MySQL.query.await("UPDATE nd_vehicles SET owner = ? WHERE id = ?", {playerTo.id, vehicleID}) + MySQL.query.await("UPDATE nd_vehicles SET owner = ? WHERE id = ?", {playerTo.id, vehicleId}) - if not playerOwnedVehicles[vehicleID] then return end - local veh = NetworkGetEntityFromNetworkId(playerOwnedVehicles[vehicleID].netid) + if not playerOwnedVehicles[vehicleId] then return end + local veh = NetworkGetEntityFromNetworkId(playerOwnedVehicles[vehicleId].netid) if not veh then playerFrom.notify({ title = "Ownership transfered", @@ -75,7 +75,7 @@ function NDCore.transferVehicle(vehicleID, fromSource, toSource) [playerTo.id] = true } - playerFrom.triggerEvent("ND_Vehicles:blip", playerOwnedVehicles[vehicleID].netid, false) + playerFrom.triggerEvent("ND_Vehicles:blip", playerOwnedVehicles[vehicleId].netid, false) playerFrom.notify({ title = "Ownership transfered", @@ -162,13 +162,13 @@ local function getVehicleType(model) return entityType end -function NDCore.spawnOwnedVehicle(source, vehicleID, coords, heading) +function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) local player = NDCore.getPlayer(source) if not player then return end local vehicles = getVehicles(player.id) for _, vehicle in pairs(vehicles) do - if vehicle.id == vehicleID and vehicle.owner == player.id then - MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleID}) + if vehicle.id == vehicleId and vehicle.owner == player.id then + MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) vehicle.available = false player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) From 0de7b9c682ea4dc9f234efe64e8f277b28d8258b Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:04:16 +0200 Subject: [PATCH 082/161] tweak(server/player): groups checks & admins from config --- server/player.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/server/player.lua b/server/player.lua index c92a32c..3a76963 100644 --- a/server/player.lua +++ b/server/player.lua @@ -261,6 +261,11 @@ local function createCharacterTable(info) local char = NDCore.players[self.source] if char and char.id == self.id then return true end if char then char.unload() end + for identifierType, identifier in pairs(self.identifiers) do + if lib.table.contains(Config.admins, ("%s:%s"):format(identifierType, identifier)) then + self.addGroup("admin") + end + end for name, _ in pairs(self.groups) do lib.addPrincipal(self.source, ("group.%s"):format(name)) end @@ -277,21 +282,21 @@ local function createCharacterTable(info) function self.addGroup(name, rank, isJob) local groupRank = tonumber(rank) or 1 local groupInfo = Config.groups[name] - if not groupInfo then return end + -- if not groupInfo then return end if isJob then for _, group in pairs(self.groups) do group.isJob = nil end end self.groups[name] = { - label = groupInfo.label, - rankName = groupInfo.ranks[groupRank] or groupRank, + label = groupInfo and groupInfo.label or name, + rankName = groupInfo and groupInfo.ranks[groupRank] or groupRank, rank = groupRank, isJob = isJob } self.triggerEvent("ND:updateCharacter", self) lib.addPrincipal(self.source, ("group.%s"):format(name)) - return true + return self.groups[name] end ---@param name string @@ -302,9 +307,11 @@ local function createCharacterTable(info) ---@param name string function self.removeGroup(name) + local group = self.groups[name] self.groups[name] = nil self.triggerEvent("ND:updateCharacter", self) lib.removePrincipal(self.source, ("group.%s"):format(name)) + return group end ---@param name string From ec86f6ff86a8e9e20daf2d721e22909b3b2198bb Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:04:25 +0200 Subject: [PATCH 083/161] Update main.lua --- server/main.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/main.lua b/server/main.lua index c448621..b1b16af 100644 --- a/server/main.lua +++ b/server/main.lua @@ -20,10 +20,11 @@ Config = { randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, - groups = json.decode(GetConvar("core:groups", "[]")) + groups = json.decode(GetConvar("core:groups", "[]")), + admins = json.decode(GetConvar("core:admins", "[]")) } -SetConvarServerInfo("ND_Core", GetResourceMetadata(resourceName, "version", 0) or "invalid") +SetConvarServerInfo("NDCore", GetResourceMetadata(resourceName, "version", 0) or "invalid") SetConvarReplicated("inventory:framework", "nd") local function getIdentifierList(src) From b7237729f427a9c5d35080f4050bea1b9ef0dbf4 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:06:01 +0200 Subject: [PATCH 084/161] tweak(server/comands): setmoney help & player check --- server/commands.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/commands.lua b/server/commands.lua index 12d583d..a0d09be 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -35,16 +35,16 @@ lib.addCommand("setmoney", { }, { name = "amount", - type = "number", - help = "bank/cash" + type = "number" } - }, + } }, function(source, args, raw) local action = moneyActions[args.action] local moneyType = args.type:lower() if not action or not lib.table.contains(moneyTypes, moneyType) then return end local player = NDCore.getPlayer(args.target) + if not player then return end local staffMessage, userMessage = action(player, moneyType, args.amount) player.notify({ From 151961b401815fdfbe3425bbd39b6b02f8b9732d Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:06:30 +0200 Subject: [PATCH 085/161] feat(server/commands): setjob --- server/commands.lua | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/server/commands.lua b/server/commands.lua index a0d09be..5f8d1a9 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -61,3 +61,44 @@ lib.addCommand("setmoney", { args = {"Staff action", staffMessage} }) end) + +lib.addCommand("setjob", { + help = "Admin command, set a players job.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + }, + { + name = "job", + type = "string", + help = "Job name" + }, + { + name = "rank", + type = "number", + optional = true + } + } +}, function(source, args, raw) + local player = NDCore.getPlayer(args.target) + local job = args.job:lower() + local jobInfo = player.setJob(job, args.rank) + if not player or not jobInfo then return end + player.notify({ + title = "Staff action", + description = ("Job updated to %s, rank %s"):format(jobInfo.label, jobInfo.rankName), + type = "inform", + duration = 10000 + }) + + if not source then return end + TriggerClientEvent("chat:addMessage", source, { + color = {50, 100, 235}, + multiline = true, + args = {"Staff action", "success"} + }) +end) + From 0ee970077710441e09010d145eb5891bce11151b Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:06:43 +0200 Subject: [PATCH 086/161] feat(server/commands): setgroup --- server/commands.lua | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/server/commands.lua b/server/commands.lua index 5f8d1a9..646f116 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -102,3 +102,62 @@ lib.addCommand("setjob", { }) end) +lib.addCommand("setgroup", { + help = "Admin command, set a players group.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + }, + { + name = "action", + type = "string", + help = "remove/add" + }, + { + name = "group", + type = "string", + help = "group name" + }, + { + name = "rank", + type = "number", + optional = true + } + } +}, function(source, args, raw) + local player = NDCore.getPlayer(args.target) + if not player then return end + + if args.action == "add" then + local groupInfo = player.addGroup(args.group, args.rank) + if not groupInfo then return end + player.notify({ + title = "Staff action", + description = ("Added to group %s, rank %s."):format(groupInfo.label, groupInfo.rankName), + type = "inform", + duration = 10000 + }) + elseif args.action == "remove" then + local groupInfo = player.removeGroup(args.group) + if not groupInfo then return end + player.notify({ + title = "Staff action", + description = ("Removed from group %s."):format(groupInfo.label), + type = "inform", + duration = 10000 + }) + else + return + end + + if not source then return end + TriggerClientEvent("chat:addMessage", source, { + color = {50, 100, 235}, + multiline = true, + args = {"Staff action", "success"} + }) +end) + From 09c9ca254d54f1e4cb301c046fc6daec5134de7c Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:06:56 +0200 Subject: [PATCH 087/161] feat(server/commands): skin --- server/commands.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/commands.lua b/server/commands.lua index 646f116..a9abf05 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -161,3 +161,17 @@ lib.addCommand("setgroup", { }) end) +lib.addCommand("skin", { + help = "Admin command, set player into character clothing menu.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + TriggerClientEvent("ND:clothingMenu", args.target) +end) + From 7f5291517e4f0b088aa3478fd57ddf1288b7caea Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:07:03 +0200 Subject: [PATCH 088/161] feat(server/commands): character --- server/commands.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/commands.lua b/server/commands.lua index a9abf05..91f733a 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -175,3 +175,17 @@ lib.addCommand("skin", { TriggerClientEvent("ND:clothingMenu", args.target) end) +lib.addCommand("character", { + help = "Admin command, set player into character selection menu.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + TriggerClientEvent("ND:characterMenu", args.target) +end) + From 8eac50be2da61e3cc63e80e66fae10b35db2c60e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:07:14 +0200 Subject: [PATCH 089/161] feat(server/commands): pay cash to nearby player --- server/commands.lua | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/server/commands.lua b/server/commands.lua index 91f733a..125c61e 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -189,3 +189,49 @@ lib.addCommand("character", { TriggerClientEvent("ND:characterMenu", args.target) end) +lib.addCommand("pay", { + help = "give money to nearby player.", + params = { + { + name = "amount", + type = "number" + } + } +}, function(source, args, raw) + if not source then return end + local targetPlayer + local pedCoords = GetEntityCoords(GetPlayerPed(source)) + for targetId, targetInfo in pairs(NDCore.players) do + local targetCoords = GetEntityCoords(GetPlayerPed(targetId)) + if #(pedCoords-targetCoords) < 2.0 and targetId ~= source then + targetPlayer = targetInfo + break + end + end + + local player = NDCore.getPlayer(source) + local success = player.deductMoney("cash", args.amount) + + if not success then + return player.notify({ + title = "Couldn't give money", + type = "error", + duration = 5000 + }) + end + + if not targetPlayer or not targetPlayer.addMoney("cash", args.amount) then return end + targetPlayer.notify({ + title = "Money received", + description = ("Received $%d in cash"):format(args.amount), + type = "inform", + duration = 10000 + }) + + player.notify({ + title = "Money given", + description = ("You gave someone $%d in cash"):format(args.amount), + type = "inform", + duration = 10000 + }) +end) From 76ae3a1c4411d144abc9c5a4c41fc75adcde5e1c Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 29 Aug 2023 03:20:04 +0200 Subject: [PATCH 090/161] tweak(server/player): return before setting active --- server/player.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/player.lua b/server/player.lua index 3a76963..e14d56d 100644 --- a/server/player.lua +++ b/server/player.lua @@ -259,7 +259,7 @@ local function createCharacterTable(info) -- Set the character as the players active character/currently playing character function self.active() local char = NDCore.players[self.source] - if char and char.id == self.id then return true end + if char and char.id == self.id then return end if char then char.unload() end for identifierType, identifier in pairs(self.identifiers) do if lib.table.contains(Config.admins, ("%s:%s"):format(identifierType, identifier)) then @@ -272,7 +272,6 @@ local function createCharacterTable(info) NDCore.players[self.source] = self TriggerEvent("ND:characterLoaded", self) self.triggerEvent("ND:characterLoaded", self) - return true end ---@param name string @@ -438,8 +437,12 @@ end ---@param id number ---@return table function NDCore.setActiveCharacter(src, id) - if not src then return end + local char = NDCore.players[src] + if not src or char and char.id == id then return end + local character = NDCore.fetchCharacter(id, src) + if not character then return end + character.name = GetPlayerName(src) character.active() return character From b09637d68cc6f02a9d139148042b9005e1c15970 Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 29 Aug 2023 03:41:07 +0200 Subject: [PATCH 091/161] Update README.md --- README.md | 103 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4039f51..209e72e 100644 --- a/README.md +++ b/README.md @@ -7,22 +7,97 @@
Documentation ## Dependency: -[oxmysql](https://github.com/overextended/oxmysql/releases/download/v2.4.0/oxmysql.zip) +* [oxmysql](https://github.com/overextended/oxmysql/releases) +* [ox_lib](https://github.com/overextended/ox_lib/releases) ## Addons - -* [Ox Inventory](https://github.com/overextended/ox_inventory/releases) -* [Character Selection](https://github.com/ND-Framework/ND_CharacterSelection) -* [No Characters (use framework without character selection)](https://github.com/ND-Framework/ND_NoCharacters) -* [Vehicle System](https://github.com/ND-Framework/ND_VehicleSystem) -* [Dealerships](https://github.com/ND-Framework/ND_Dealership) -* [Fuel (with nozzle and hose)](https://github.com/ND-Framework/ND_Fuel) -* [Nitro](https://github.com/ND-Framework/ND_Nitro) -* [Shot Spotter](https://github.com/ND-Framework/ND_ShotSpotter) -* [Banking](https://github.com/ND-Framework/ND_Banking) -* [Doorlocks](https://github.com/ND-Framework/ND_Doorlocks) -* [Properties](https://github.com/ND-Framework/ND_Properties) -* [Appearance Shops](https://github.com/ND-Framework/ND_AppearanceShops) +* [Character Selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2) # Need support? [](https://discord.gg/Z9Mxu72zZ6) + +# Setup until docs is done: +Paste code below into a file and name it ndcore.cfg then write `exec ndcore.cfg` in your server.cfg +```py +# Your servers name +setr core:serverName "My FiveM Server" + +# Discord invite link +setr core:discordInvite "https://discord.gg/Z9Mxu72zZ6" + +# Discord app id for rich presence +setr core:discordAppId "858146067018416128" + +# Images for discord rich presence +setr core:discordAsset "andyyy" +setr core:discordAssetSmall "andyyy" + +# Buttons for discord rich presence +setr core:discordActionText "DISCORD" +setr core:discordActionLink "https://discord.gg/Z9Mxu72zZ6" + +setr core:discordActionText2 "STORE" +setr core:discordActionLink2 "https://andyyy.tebex.io/category/fivem-scripts" + +# Used for getting users roles from your server, this can be useful for discord based scripts, if you don't add then it won't be used. +# set core:discordGuildId "123456789012345678" +# set core:discordBotToken "EXAMPLE_TOKEN.abc123.xyz456" + +# The identifier to use for characters. Players aren't allowed to join without it, license is good don't change unless you know what you're doing. +set core:characterIdentifier "license" + +# % chance of random vehicles being unlocked. +setr core:randomUnlockedVehicleChance 30 + +# disable vehicle air contorl for cars and other land vehicles that's not supposed to do flips in air. +setr core:disableVehicleAirControl true + +# If true it will use ox_inventory keys item for vehicles, if false it will use a keybind. +setr core:useInventoryForKeys true + +# You can set admins here by their identifiers, admins will receive admin group in core and have access to group.admin ace perms. +# Admins get access to commands and more. +set core:admins ["fivem:1459624"] + +# Allow ox_lib to use commands, don't remove this. +add_ace resource.ox_lib command.add_ace allow +add_ace resource.ox_lib command.remove_ace allow +add_ace resource.ox_lib command.add_principal allow +add_ace resource.ox_lib command.remove_principal allow + +# This is jobs, gangs, police, fire, ambulance, everything. +setr core:groups { + "sahp": { + "label": "SAHP", + "ranks": ["Trooper", "Senior Trooper", "Corporal", "Sergeant", "Lieutenant", "Chief"] + }, + "lspd": { + "label": "LSPD", + "ranks": ["Officer", "Senior officer", "Corporal", "Sergeant", "Lieutenant", "Chief"] + }, + "bcso": { + "label": "BCSO", + "ranks": ["Officer", "Senior officer", "Corporal", "Sergeant", "Lieutenant", "Chief"] + }, + "swat": { + "label": "SWAT", + "ranks": ["Member", "Sniper", "Team lead", "Commander"] + }, + "lsfd": { + "label": "LSFD", + "ranks": ["Volunteer", "Firefighter", "Senior firefighter", "Lieutenant", "Fire Chief"] + }, + "ballas": { + "label": "Ballas", + "ranks": ["Member", "Leader"] + }, + "families": { + "label": "Families", + "ranks": ["Member", "Leader"] + }, + "cartel": { + "label": "Madrazo Cartel", + "ranks": ["Member", "Leader"] + } +} +``` From 8cc7be3e4b2fb0dd9bde589598d485a9d90ad396 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 30 Aug 2023 01:21:53 +0200 Subject: [PATCH 092/161] feat(server/main): discord invite link in server info --- server/main.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/server/main.lua b/server/main.lua index b1b16af..3c8ead8 100644 --- a/server/main.lua +++ b/server/main.lua @@ -24,6 +24,7 @@ Config = { admins = json.decode(GetConvar("core:admins", "[]")) } +SetConvarServerInfo("Discord", Config.discordInvite) SetConvarServerInfo("NDCore", GetResourceMetadata(resourceName, "version", 0) or "invalid") SetConvarReplicated("inventory:framework", "nd") From c701622172ed1abf9fdd5a3cc89d0da33884a9e3 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 30 Aug 2023 22:17:00 +0200 Subject: [PATCH 093/161] refactor(client/vehicle): move variables to top --- client/vehicle.lua | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 8cf71c1..13e856c 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -185,6 +185,22 @@ local vehicleClassNames = { [22] = "Open wheel" } +local cruiseSpeedSet = 0 +local cruiseSpeedVehicle = 0 +local cruiseControlEnabled = false +local playerVehicle = cache.seat == -1 and cache.vehicle +local cloudTime = GetCloudTimeAsInt() + +local vehicleLockCheckTime = { + lastCheck = cloudTime, + lastUse = cloudTime +} + +local keyCheckTime = { + lastCheck = cloudTime, + hasKey = false +} + local vehicleClassNotDisableAirControl = { [8] = true, --motorcycle [13] = true, --bicycles @@ -348,17 +364,6 @@ lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) return ("%s %s"):format(make, name) end) -local playerVehicle = cache.seat == -1 and cache.vehicle -local cloudTime = GetCloudTimeAsInt() -local vehicleLockCheckTime = { - lastCheck = cloudTime, - lastUse = cloudTime -} -local keyCheckTime = { - lastCheck = cloudTime, - hasKey = false -} - local function hasVehicleKeys(veh) local state = Entity(veh).state if Config.ox_inventory and Config.useInventoryForKeys then @@ -632,10 +637,6 @@ exports("keyControl", function(action, slot) end end) -local cruiseSpeedSet = 0 -local cruiseSpeedVehicle = 0 -local cruiseControlEnabled = false - local function cruiseControl() cruiseSpeedVehicle = GetEntitySpeed(playerVehicle) * 2.236936 if not playerVehicle then From 70866dd4b9251e445e2628417b00e5bd9284c8a7 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 30 Aug 2023 22:18:26 +0200 Subject: [PATCH 094/161] fix(client/vehicle): check engine hotwire with param --- client/vehicle.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 13e856c..ee63a11 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -286,7 +286,7 @@ local function playKeyFob(veh) local coords = GetEntityCoords(ped) if #(coords-GetEntityCoords(veh)) > 25.0 then return end - if GetVehiclePedIsIn(ped) == 0 then + if not playerVehicle then ClearPedTasks(ped) lib.requestAnimDict("anim@mp_player_intmenu@key_fob@") TaskPlayAnim(ped, "anim@mp_player_intmenu@key_fob@", "fob_click_fp", 8.0, 8.0, -1, 48, 1, false, false, false) @@ -364,7 +364,7 @@ lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) return ("%s %s"):format(make, name) end) -local function hasVehicleKeys(veh) +local function hasVehicleKeys(veh, checkEngine) local state = Entity(veh).state if Config.ox_inventory and Config.useInventoryForKeys then local metadata = { @@ -372,13 +372,13 @@ local function hasVehicleKeys(veh) keyEnabled = true } local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0 - return hasKey or state.hotwired + return hasKey or checkEngine and state.hotwired end local keys = state and state.keys local player = NDCore.getPlayer() local hasKey = player and keys and keys[player.id] - return hasKey or state.hotwired + return hasKey or checkEngine and state.hotwired end local function hasVehicleKeysCheck(veh) @@ -387,7 +387,7 @@ local function hasVehicleKeysCheck(veh) return keyCheckTime.hasKey end - local hasKey = hasVehicleKeys(veh) + local hasKey = hasVehicleKeys(veh, true) keyCheckTime.lastCheck = time keyCheckTime.hasKey = hasKey return hasKey @@ -614,10 +614,10 @@ exports("keyControl", function(action, slot) break end end + exports.ox_inventory:closeInventory() if action == "trunk" then - local veh = getNearestVehicle(hasKeysForOnly) + local veh = getNearestVehicle(true) if not veh then return end - exports.ox_inventory:closeInventory() playKeyFob(veh) if GetVehicleDoorAngleRatio(veh, 5) > 0.0 then SetVehicleDoorShut(veh, 5) @@ -626,7 +626,6 @@ exports("keyControl", function(action, slot) end elseif action == "disable" then TriggerServerEvent("ND_Vehicles:disableKey", slot) - exports.ox_inventory:closeInventory() lib.notify({ title = "Key disabled", description = "This vehicle key has been disabled and cannot be used anymore.", From 0c70cd42dcb651d19ae2ed8e9218c7b70b9bf028 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 31 Aug 2023 02:53:34 +0200 Subject: [PATCH 095/161] update(server/player): full name to player --- server/player.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/server/player.lua b/server/player.lua index e14d56d..a6d1baa 100644 --- a/server/player.lua +++ b/server/player.lua @@ -10,6 +10,7 @@ local function createCharacterTable(info) name = info.name, firstname = info.firstname, lastname = info.lastname, + fullname = ("%s %s"):format(info.firstname, info.lastname), dob = info.dob, gender = info.gender, cash = info.cash, From 00563a5f41ae914c4cd54f597202102a8456f112 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Thu, 31 Aug 2023 05:56:31 +0200 Subject: [PATCH 096/161] feat(client): ai peds --- client/peds.lua | 174 ++++++++++++++++++++++++++++++++++++++++++++++++ fxmanifest.lua | 1 + 2 files changed, 175 insertions(+) create mode 100644 client/peds.lua diff --git a/client/peds.lua b/client/peds.lua new file mode 100644 index 0000000..7f7e08f --- /dev/null +++ b/client/peds.lua @@ -0,0 +1,174 @@ +local target, ox_target +local locations = {} +local clothingComponents = { + face = 0, + mask = 1, + hair = 2, + torso = 3, + leg = 4, + bag = 5, + shoes = 6, + accessory = 7, + undershirt = 8, + kevlar = 9, + badge = 10, + torso2 = 11 +} +local clothingProps = { + hat = 0, + glasses = 1, + ears = 2, + watch = 6, + bracelets = 7 +} + +NDCore.isResourceStarted("ox_target", function(started) + target = started + if not target then return end + ox_target = exports.ox_target +end) + +local function configPed(ped) + SetPedCanBeTargetted(ped, false) + SetEntityCanBeDamaged(ped, false) + SetBlockingOfNonTemporaryEvents(ped, true) + SetPedCanRagdollFromPlayerImpact(ped, false) + SetPedResetFlag(ped, 249, true) + SetPedConfigFlag(ped, 185, true) + SetPedConfigFlag(ped, 108, true) + SetPedConfigFlag(ped, 208, true) + SetPedCanRagdoll(ped, false) +end + +local function setClothing(ped, clothing) + if not clothing then return end + for component, clothingInfo in pairs(clothing) do + if clothingComponents[component] then + SetPedComponentVariation(ped, clothingComponents[component], clothingInfo.drawable, clothingInfo.texture, 0) + elseif clothingProps[component] then + SetPedPropIndex(ped, clothingProps[component], clothingInfo.drawable, clothingInfo.texture, true) + end + end +end + +local function createBlip(coords, info) + if not info then return end + local blip = AddBlipForCoord(coords.x, coords.y, coords.z) + SetBlipSprite(blip, info.sprite or 280) + SetBlipScale(blip, info.scale or 0.8) + SetBlipColour(blip, info.color or 3) + SetBlipAsShortRange(blip, true) + if info.label then + BeginTextCommandSetBlipName("STRING") + AddTextComponentString(info.label) + EndTextCommandSetBlipName(blip) + end + return blip +end + +function NDCore.createAiPed(info) + local ped + local model = type(info.model) == "string" and GetHashKey(info.model) or info.model + local blipInfo = info.blip + local anim = info.anim + local clothing = info.clothing + local coords = info.coords + local options = info.options + local blip = createBlip(coords, blipInfo) + local point = lib.points.new({ + coords = vec3(coords.x, coords.y, coords.z), + distance = info.distance or 25.0 + }) + + local id = #locations+1 + locations[id] = { + point = point, + blip = blip, + options = info.options, + resource = GetInvokingResource() + } + + local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true) + lib.requestModel(model) + + function point:onEnter() + ped = CreatePed(4, model, coords.x, coords.y, found and ground or coords.z, coords.w or coords.h or info.heading, false, false) + local timeOut = 100 + while not DoesEntityExist(ped) and timeOut > 0 do + Wait(10) + timeOut -= 1 + end + + configPed(ped) + setClothing(ped, clothing) + locations[id].ped = ped + + if anim and anim.dict and anim.clip then + lib.requestAnimDict(anim.dict) + TaskPlayAnim(ped, anim.dict, anim.clip, 2.0, 8.0, -1, 1, 0, 0, 0, 0) + end + + if target and options then + ox_target:addLocalEntity({ped}, options) + end + end + + function point:onExit() + if ped and DoesEntityExist(ped) then + if target and options then + ox_target:removeLocalEntity({ped}) + end + Wait(500) + DeleteEntity(ped) + end + end + + return id +end + +function NDCore.removeAiPed(id) + local info = locations[id] + if not info then return end + + local ped = info.ped + local blip = info.blip + info.point:remove() + locations[id] = nil + + if blip and DoesBlipExist(blip) then + RemoveBlip(blip) + end + + if ped and DoesEntityExist(ped) then + if info.options then + ox_target:removeLocalEntity({ped}) + end + Wait(500) + DeleteEntity(ped) + end +end + +AddEventHandler("onResourceStop", function(name) + if name == GetCurrentResourceName() then + for i, _ in ipairs(locations) do + NDCore.removeAiPed(i) + end + else + for i, v in ipairs(locations) do + if v.resource == name then + NDCore.removeAiPed(i) + end + end + end +end) + +RegisterCommand("getclothing", function(source, args, rawCommand) + local info = "" + for k, v in pairs(clothingComponents) do + info = ("%s\n%s = {\n drawable = %s,\n texture = %s\n},"):format(info, k, GetPedDrawableVariation(cache.ped, v), GetPedTextureVariation(cache.ped, v)) + end + for k, v in pairs(clothingProps) do + info = ("%s\n%s = {\n drawable = %s,\n texture = %s\n},"):format(info, k, GetPedPropIndex(cache.ped, v), GetPedPropTextureIndex(cache.ped, v)) + end + lib.setClipboard(info) +end, false) diff --git a/fxmanifest.lua b/fxmanifest.lua index 264e9f4..3199f27 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -13,6 +13,7 @@ client_scripts { "client/main.lua", "shared/functions.lua", "client/vehicle.lua", + "client/peds.lua", "client/functions.lua", "client/events.lua", "compatibility/**/client.lua" From 3dd2daad572f1348c0f832b6a4a5be5f9c1dda1e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 18 Sep 2023 22:53:23 +0200 Subject: [PATCH 097/161] fix(client/vehicle): don't lock/unlock non set --- client/vehicle.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index ee63a11..f10aec6 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -329,6 +329,8 @@ AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, CreateThread(function() while GetVehiclePedIsEntering(cache.ped) == entity do Wait(10) end -- SetVehicleDoorsLockedForAllPlayers(entity, false) + + if value == nil then return end SetVehicleDoorsLocked(entity, 0) end) end) From 82bc4c6d6b0c3a12b429cf8f0d2a6e09b54f5180 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 18 Sep 2023 22:54:05 +0200 Subject: [PATCH 098/161] feat(client/functions): notification based on resource --- client/functions.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/functions.lua b/client/functions.lua index bd18218..9cd34f2 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -26,6 +26,14 @@ function NDCore.getPlayersFromCoords(distance, coords) return closePlayers end +function NDCore.notify(...) + if GetResourceState("ModernHUD") == "started" then + exports["ModernHUD"]:notify(...) + elseif GetResourceState("ox_lib") == "started" then + lib.notify(...) + end +end + for name, func in pairs(NDCore) do if type(func) == "function" then exports(name, func) From 7a978165706a40e550ab538a79aade53d2720a40 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 04:45:37 +0200 Subject: [PATCH 099/161] tweak: remove character functions on server --- client/events.lua | 18 ++++++------------ server/player.lua | 22 ++++++++++++++++------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/client/events.lua b/client/events.lua index aa31e2f..54bd635 100644 --- a/client/events.lua +++ b/client/events.lua @@ -5,24 +5,14 @@ RegisterNetEvent("ND:updateMoney", function(cash, bank) NDCore.player.bank = bank end) -local function removeCharacterFunctions(character) - local newData = {} - for k, v in pairs(character) do - if type(v) ~= "function" then - newData[k] = v - end - end - return newData -end - -- Sets main character. RegisterNetEvent("ND:characterLoaded", function(character) - NDCore.player = removeCharacterFunctions(character) + NDCore.player = character end) -- Update main character info. RegisterNetEvent("ND:updateCharacter", function(character) - NDCore.player = removeCharacterFunctions(character) + NDCore.player = character end) -- Updates last lcoation. @@ -30,3 +20,7 @@ RegisterNetEvent("ND:updateLastLocation", function(location) if not NDCore.player then return end NDCore.player.lastLocation = location end) + +RegisterNetEvent("ND:revivePlayer", function() + NDCore.revivePlayer(true) +end) diff --git a/server/player.lua b/server/player.lua index a6d1baa..8b0fa0e 100644 --- a/server/player.lua +++ b/server/player.lua @@ -1,3 +1,13 @@ +local function removeCharacterFunctions(character) + local newData = {} + for k, v in pairs(character) do + if type(v) ~= "function" then + newData[k] = v + end + end + return newData +end + local function createCharacterTable(info) local playerInfo = PlayersInfo[info.source] or {} @@ -102,7 +112,7 @@ local function createCharacterTable(info) TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason) end end - self.triggerEvent("ND:updateCharacter", self) + self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) end ---@param key string|table @@ -116,7 +126,7 @@ local function createCharacterTable(info) else self.metadata[key] = value end - self.triggerEvent("ND:updateCharacter", self) + self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) return self.metadata end @@ -197,7 +207,7 @@ local function createCharacterTable(info) else self.metadata.licenses = {license} end - self.triggerEvent("ND:updateCharacter", self) + self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) end function self.getLicense(identifier) @@ -272,7 +282,7 @@ local function createCharacterTable(info) end NDCore.players[self.source] = self TriggerEvent("ND:characterLoaded", self) - self.triggerEvent("ND:characterLoaded", self) + self.triggerEvent("ND:characterLoaded", removeCharacterFunctions(self)) end ---@param name string @@ -294,7 +304,7 @@ local function createCharacterTable(info) rank = groupRank, isJob = isJob } - self.triggerEvent("ND:updateCharacter", self) + self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) lib.addPrincipal(self.source, ("group.%s"):format(name)) return self.groups[name] end @@ -309,7 +319,7 @@ local function createCharacterTable(info) function self.removeGroup(name) local group = self.groups[name] self.groups[name] = nil - self.triggerEvent("ND:updateCharacter", self) + self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) lib.removePrincipal(self.source, ("group.%s"):format(name)) return group end From ae7f0dc0d822bb245034e1a8d2966dada389c82f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 04:46:24 +0200 Subject: [PATCH 100/161] fix(client/vehicle): locking issues --- client/vehicle.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index f10aec6..0e94b37 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -318,9 +318,8 @@ local dontLock = { AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, replicated) local entity = GetEntityFromStateBagName(bagName) - if entity == 0 then return end - if dontLock[GetVehicleClass(entity)] then return end - + if entity == 0 or value == nil or dontLock[GetVehicleClass(entity)] then return end + if value then -- SetVehicleDoorsLockedForAllPlayers(entity, true) return SetVehicleDoorsLocked(entity, 2) @@ -330,7 +329,6 @@ AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, while GetVehiclePedIsEntering(cache.ped) == entity do Wait(10) end -- SetVehicleDoorsLockedForAllPlayers(entity, false) - if value == nil then return end SetVehicleDoorsLocked(entity, 0) end) end) @@ -403,7 +401,7 @@ local function getNearestVehicle(hasKeysForOnly) local function setNearestVehicle(veh) if hasKeysForOnly and not hasVehicleKeys(veh.vehicle) then return end local nearestDist = nearestVeh.dist - local dist = #(GetEntityCoords(cache.ped)-veh.coords) + local dist = #(coords-veh.coords) if not nearestDist or dist < nearestDist then nearestVeh.dist = dist nearestVeh.coords = veh.coords From 42cd797cc0bb6860e230f60330e1a5d682bd330c Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:13:31 +0200 Subject: [PATCH 101/161] tweak(server/functions): get players by id --- server/functions.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/server/functions.lua b/server/functions.lua index 6721992..fd0b19b 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -7,16 +7,22 @@ end ---@param metadata string ---@param data any ---@return table -function NDCore.getPlayers(key, value) +function NDCore.getPlayers(key, value, returnArray) if not key or not value then return NDCore.players end local players = {} - local keyTypes = {firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"} + local keyTypes = {id = "id", firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"} local findBy = keyTypes[key] or "metadata" - for src, info in pairs(NDCore.players) do - if info[findBy][key] == value then - players[src] = info + if findBy then + for src, info in pairs(NDCore.players) do + if findBy == "metadata" and info["metadata"][key] == value or info[findBy] == value then + if returnArray then + players[#players+1] = info + else + players[src] = info + end + end end end return players From 166baead7478082847a26802d10966a06a21ac01 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:14:26 +0200 Subject: [PATCH 102/161] fix(client/vehicle): NULL vehicle label --- client/vehicle.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/vehicle.lua b/client/vehicle.lua index 0e94b37..0803cc7 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -361,6 +361,11 @@ end) lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) local make = GetLabelText(GetMakeNameFromVehicleModel(model)) local name = GetLabelText(GetDisplayNameFromVehicleModel(model)) + if make == "NULL" then + return name + elseif name == "NULL" then + return make + end return ("%s %s"):format(make, name) end) From c328a8dd5029df97add34f790b7f88e7553e7b40 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:16:53 +0200 Subject: [PATCH 103/161] refactor(vehicle): new functions & optimize --- client/vehicle.lua | 12 -- server/vehicle.lua | 298 ++++++++++++++++++++++++++++++++------------- 2 files changed, 211 insertions(+), 99 deletions(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index 0803cc7..afb7da3 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -346,18 +346,6 @@ lib.callback.register("ND_Vehicles:getProps", function() return props end) -lib.callback.register("ND_Vehicles:getNearbyVehicleById", function(vehId) - local coords = GetEntityCoords(cache.ped) - local vehicles = lib.getNearbyVehicles(coords, 25.0, true) - for i=1, #vehicles do - local veh = vehicles[i] - local state = Entity(veh.vehicle).state - if state and state.id == vehId then - return VehToNet(veh.vehicle) - end - end -end) - lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model) local make = GetLabelText(GetMakeNameFromVehicleModel(model)) local name = GetLabelText(GetDisplayNameFromVehicleModel(model)) diff --git a/server/vehicle.lua b/server/vehicle.lua index 2737f42..926c20b 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -8,42 +8,200 @@ NDCore.isResourceStarted("ox_inventory", function(started) ox_inventory = exports.ox_inventory end) -local function isPlateAvailable(plate) - return not MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) +local function getVehicleType(model) + local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, true, true) + + local time = os.time() + while not DoesEntityExist(tempVehicle) and time-os.time() < 5 do Wait(5) end + + if not DoesEntityExist(tempVehicle) then return end + local entityType = GetVehicleType(tempVehicle) + DeleteEntity(tempVehicle) + return entityType end local function generatePlate() local plate = {} - for i = 1, 8 do + for i=1, 8 do plate[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) end return table.concat(plate) end -local function generatePlateWait() - local plate = generatePlate() - while not isPlateAvailable(plate) do +local function generateVehiclePlate(newPlate) + local plate = newPlate or generatePlate() + while MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) do plate = generatePlate() end return plate end -local function getVehicles(characterId) +local function createVehicleInfo(info) + local entity = info.entity + + function info.delete() + if not DoesEntityExist(entity) then return end + DeleteEntity(entity) + end + + function info.properties(props) + if not DoesEntityExist(entity) then return end + local state = Entity(entity).state + state.props = type(props) == "string" and json.decode(props) or props + end + + function info.plate(plate) + if not DoesEntityExist(entity) or MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) then return end + SetVehicleNumberPlateText(entity, plate) + return true + end + + return info +end + +function NDCore.getVehicle(entity) + if not DoesEntityExist(entity) then return end + + local state = Entity(entity).state + local vehicleId = state.id + if not vehicleId then return end + + return createVehicleInfo({ + entity = entity, + owner = state.owner, + id = vehicleId, + properties = state.props, + locked = state.locked, + keys = state.keys, + hotwired = state.hotwired, + metadata = state.metadata + }) +end + +function NDCore.getVehicles(characterId) local result = MySQL.query.await("SELECT * FROM nd_vehicles WHERE owner = ?", {characterId}) if not result then return {} end + local vehicles = {} for _, vehicle in pairs(result) do - local key = #vehicles + 1 - vehicles[key] = {} - vehicles[key].available = vehicle.stored == 1 - vehicles[key].owner = vehicle.owner - vehicles[key].id = vehicle.id - vehicles[key].plate = vehicle.plate - vehicles[key].properties = json.decode(vehicle.properties) + vehicles[#vehicles+1] = { + available = vehicle.stored == 1, + owner = vehicle.owner, + id = vehicle.id, + plate = vehicle.plate, + properties = json.decode(vehicle.properties) + } end return vehicles end +function NDCore.giveVehicleAccess(source, vehicle, access, vehicleId, netId, plate, model, name, owner) + if inventoryStarted and Config.useInventoryForKeys and (not vehicleId or ox_inventory:GetItem(source, "keys", {vehId = vehicleId}, true) == 0) and (not netId or ox_inventory:GetItem(source, "keys", {vehNetId = netId}, true) == 0) then + if access then + ox_inventory:AddItem(source, "keys", 1, { + vehOwner = owner, + vehId = vehicleId, + vehPlate = plate, + vehModel = name or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "", + keyEnabled = true, + vehNetId = netId + }) + else + ox_inventory:RemoveItem(source, "keys", 1, { + vehOwner = owner, + vehId = vehicleId, + vehPlate = plate, + vehModel = name or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "", + vehNetId = netId + }) + end + end + + local player = NDCore.getPlayer(source) + if not player then return end + + local state = Entity(vehicle).state + if not state.keys then + state.keys = { + [player.id] = access + } + else + local keys = state.keys + keys[player.id] = access + state.keys = keys + end +end + +function NDCore.createVehicle(info) + local owner = info.owner + local vehicleId = info.vehicleId + local properties = info.properties or {} + local coords = info.coords + local spawnCoords = coords + local coordType = type(coords) + if coordType == "vector3" or coordType == "table" then + spawnCoords = vector4(coords.x, coords.y, coords.z, coords.w or coords.h or info.heading or 0.0) + end + if not spawnCoords then return end + + local model = info.model or properties.model + local vehType = getVehicleType(model) + if not vehType then return end + + local veh = CreateVehicleServerSetter(model, vehType, spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w) + local time = os.time() + while not DoesEntityExist(veh) and time-os.time() < 5 do Wait(5) end + if not veh or not DoesEntityExist(veh) then return end + + local netId = NetworkGetNetworkIdFromEntity(veh) + local state = Entity(veh).state + state.locked = true + local keys = info.keys or {} + + if vehicleId then + playerOwnedVehicles[vehicleId] = { + netid = netId, + entity = veh + } + else + vehicleId = ("temp_%s%d"):format(string.char(math.random(65, 90)), math.random(1, 999999)) + end + if not properties.plate then + properties.plate = generateVehiclePlate() + end + if owner then + keys[owner] = true + state.owner = owner + state.keys = keys + end + + state.props = properties + state.id = vehicleId + local vehicleName + if inventoryStarted and Config.useInventoryForKeys then + for charId, _ in pairs(keys) do + local player = NDCore.getPlayers("id", charId, true)[1] + local source = player and player.source + if source then + if not vehicleName then + vehicleName = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "" + end + NDCore.giveVehicleAccess(source, veh, true, vehicleId, netId, properties and properties.plate, model, vehicleName, owner) + end + end + end + + return createVehicleInfo({ + entity = veh, + netId = netId, + owner = owner, + id = vehicleId, + properties = properties, + locked = true, + keys = keys + }) +end + function NDCore.transferVehicle(vehicleId, fromSource, toSource) local playerTo = NDCore.getPlayer(toSource) local playerFrom = NDCore.getPlayer(fromSource) @@ -95,10 +253,10 @@ end function NDCore.setVehicleOwned(src, properties, stored) local player = NDCore.getPlayer(src) - local plate = generatePlateWait() + local plate = generateVehiclePlate() properties.plate = plate local id = MySQL.insert.await("INSERT INTO nd_vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {player.id, properties.plate, json.encode(properties), stored and 1 or 0}) - local vehicles = getVehicles(player.id) + local vehicles = NDCore.getVehicles(player.id) player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) return id end @@ -134,74 +292,23 @@ function NDCore.giveVehicleKeys(vehicle, source, target) return true end -function NDCore.giveVehicleAccess(source, vehicle, access) - local state = Entity(vehicle).state - if not state then return end - - local player = NDCore.getPlayer(source) - if not player then return end - - if not state.keys then - state.keys = { - [player.id] = access - } - else - local keys = state.keys - keys[player.id] = access - state.keys = keys - end - - player.triggerEvent("ND_Vehicles:setOwnedIfNot", NetworkGetNetworkIdFromEntity(vehicle)) -end - -local function getVehicleType(model) - local tempVehicle = CreateVehicle(model, 0, 0, 0, 0, true, true) - while not DoesEntityExist(tempVehicle) do Wait(0) end - local entityType = GetVehicleType(tempVehicle) - DeleteEntity(tempVehicle) - return entityType -end - function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) local player = NDCore.getPlayer(source) if not player then return end - local vehicles = getVehicles(player.id) + local vehicles = NDCore.getVehicles(player.id) for _, vehicle in pairs(vehicles) do if vehicle.id == vehicleId and vehicle.owner == player.id then MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) vehicle.available = false player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) - - local model = vehicle.properties.model - local veh = CreateVehicleServerSetter(model, getVehicleType(model), coords.x, coords.y, coords.z, coords.w or heading) - while not DoesEntityExist(veh) do Wait(0) end - - playerOwnedVehicles[vehicle.id] = { - netid = NetworkGetNetworkIdFromEntity(veh), - entity = veh - } - - local state = Entity(veh).state - state.owner = vehicle.owner - state.id = vehicle.id - state.props = vehicle.properties - state.locked = true - - if inventoryStarted and Config.useInventoryForKeys and ox_inventory:GetItem(source, "keys", {vehId = vehicle.id}, true) == 0 then - ox_inventory:AddItem(source, "keys", 1, { - vehOwner = vehicle.owner, - vehId = vehicle.id, - vehPlate = vehicle.properties and vehicle.properties.plate, - vehModel = vehicle.properties and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, vehicle.properties.model) or "", - keyEnabled = true - }) - end - - state.keys = { - [player.id] = true - } - - return true + return NDCore.createVehicle({ + owner = vehicle.owner, + model = vehicle.properties.model, + coords = vec4(coords.x, coords.y, coords.z, coords.w or heading), + properties = vehicle.properties, + vehicleId = vehicleId, + source = source + }) end end end @@ -211,12 +318,12 @@ function NDCore.returnVehicleToGarage(source, veh, properties) if not DoesEntityExist(veh) then return end local vehID = Entity(veh).state.id - local vehicles = getVehicles(player.id) + local vehicles = NDCore.getVehicles(player.id) for _, vehicle in pairs(vehicles) do if vehicle.owner == player.id and vehicle.id == vehID then MySQL.query.await("UPDATE nd_vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(properties), 1, vehID}) - player.triggerEvent("ND_Vehicles:returnVehicles", getVehicles(player.id)) + player.triggerEvent("ND_Vehicles:returnVehicles", NDCore.getVehicles(player.id)) DeleteEntity(veh) return true end @@ -228,7 +335,7 @@ function NDCore.saveVehicleProperties(source, veh, properties) if not DoesEntityExist(veh) then return end local vehID = Entity(veh).state.id - local vehicles = getVehicles(player.id) + local vehicles = NDCore.getVehicles(player.id) for _, vehicle in pairs(vehicles) do if vehicle.owner == player.id and vehicle.id == vehID then @@ -300,21 +407,38 @@ local function toggleVehicleLock(source, veh, nearby, metadata) }) end +local function getNearbyVehicles(coords, range) + local nearby = {} + local vehicles = GetAllVehicles() + for i=1, #vehicles do + local veh = vehicles[i] + local vehCoords = GetEntityCoords(veh) + if #(coords-vehCoords) < range then + nearby[#nearby+1] = veh + end + end + return nearby +end + local function lockNearestVehicle(source, vehId, metadata) + local ped = GetPlayerPed(source) + local pedCoords = GetEntityCoords(ped) local veh = playerOwnedVehicles[vehId] and playerOwnedVehicles[vehId].entity + if veh and DoesEntityExist(veh) then - local ped = GetPlayerPed(source) - local pedCoords = GetEntityCoords(ped) local vehCoords = GetEntityCoords(veh) if not pedCoords or not vehCoords then return end return toggleVehicleLock(source, veh, #(pedCoords-vehCoords) < 25.0, metadata) end - - lib.callback("ND_Vehicles:getNearbyVehicleById", source, function(netId) - local veh = netId and NetworkGetEntityFromNetworkId(netId) - if not veh then return end - toggleVehicleLock(source, veh, true, metadata) - end, vehId) + + local vehicles = getNearbyVehicles(pedCoords, 25.0) + for i=1, #vehicles do + local veh = vehicles[i] + local state = Entity(veh).state + if state and state.id == vehId then + return toggleVehicleLock(source, veh, true, metadata) + end + end end exports("keys", function(event, item, inventory, slot, data) From 0e35b412fb2ea96a584b24c2dd632a8a1592ff06 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:17:33 +0200 Subject: [PATCH 104/161] feat(server/commands): more admin actions --- server/commands.lua | 206 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/server/commands.lua b/server/commands.lua index 125c61e..0f0b066 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -235,3 +235,209 @@ lib.addCommand("pay", { duration = 10000 }) end) + +lib.addCommand("unlock", { + help = "Admin force unlock vehicles", + restricted = "group.admin", +}, function(source, args, raw) + local ped = GetPlayerPed(source) + local playerVeh = GetVehiclePedIsIn(ped) + local coords = GetEntityCoords(ped) + local vehicles = GetAllVehicles() + local maxDistance = 2.0 + local closestVehicle + + if not playerVeh or playerVeh == 0 or not DoesEntityExist(playerVeh) then + for i=1, #vehicles do + local vehicle = vehicles[i] + local vehicleCoords = GetEntityCoords(vehicle) + local distance = #(coords-vehicleCoords) + + if distance < maxDistance then + maxDistance = distance + closestVehicle = vehicle + end + end + if not closestVehicle or not DoesEntityExist(closestVehicle) then return end + local state = Entity(closestVehicle).state + state.locked = false + else + local state = Entity(playerVeh).state + state.hotwired = true + end +end) + +lib.addCommand("revive", { + help = "Admin command, revive player.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + local player = NDCore.getPlayer(args.target) + if not player then return end + player.revive() +end) + +lib.addCommand("dv", { + help = "Admin command, revive player.", + restricted = "group.admin", + params = { + { + name = "range", + type = "number", + help = "The range to select vehicles for deleteion from", + optional = true + } + } +}, function(source, args, raw) + local ped = GetPlayerPed(source) + local veh = GetVehiclePedIsIn(ped) + if veh and veh ~= 0 and DoesEntityExist(veh) then + DeleteEntity(veh) + return TriggerClientEvent("chat:addMessage", source, { + color = {50, 100, 235}, + multiline = true, + args = {"Staff action", "deleted 1 vehicle"} + }) + end + + local count = 0 + local coords = GetEntityCoords(ped) + if args.range then + for _, veh in ipairs(GetAllVehicles()) do + local vehDist = #(GetEntityCoords(veh) - coords) + if vehDist < args.range then + DeleteEntity(veh) + count += 1 + end + end + return TriggerClientEvent("chat:addMessage", source, { + color = {50, 100, 235}, + multiline = true, + args = {"Staff action", ("deleted %d vehicles"):format(count)} + }) + end + + local closest, dist + for _, veh in ipairs(GetAllVehicles()) do + local vehDist = #(GetEntityCoords(veh) - coords) + if vehDist < 5.0 and not closest or (dist and dist > vehDist) then + closest = veh + dist = vehDist + end + end + + if not closest then return "no vehicle found nearby" end + DeleteEntity(closest) + TriggerClientEvent("chat:addMessage", source, { + color = {50, 100, 235}, + multiline = true, + args = {"Staff action", "deleted 1 vehicle"} + }) +end) + +lib.addCommand("goto", { + help = "Admin command, teleport to a player.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + if args.target == source then return end + local target = GetPlayerPed(args.target) + local coords = GetEntityCoords(target) + local ped = GetPlayerPed(source) + SetEntityCoords(ped, coords.x, coords.y, coords.z) +end) + +lib.addCommand("bring", { + help = "Admin command, teleport a player to you.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + if args.target == source then return end + local ped = GetPlayerPed(source) + local coords = GetEntityCoords(ped) + local targetPed = GetPlayerPed(args.target) + SetEntityCoords(targetPed, coords.x, coords.y, coords.z) +end) + +lib.addCommand("freeze", { + help = "Admin command, freeze a player.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + local ped = GetPlayerPed(args.target) + FreezeEntityPosition(ped, true) +end) + +lib.addCommand("unfreeze", { + help = "Admin command, unfreeze a player.", + restricted = "group.admin", + params = { + { + name = "target", + type = "playerId", + help = "Target player's server id" + } + } +}, function(source, args, raw) + local ped = GetPlayerPed(args.target) + FreezeEntityPosition(ped, false) +end) + +lib.addCommand("vehicle", { + help = "Admin command, unfreeze a player.", + restricted = "group.admin", + params = { + { + name = "model", + type = "string", + help = "Name of the vehicle to spawn" + } + } +}, function(source, args, raw) + local player = NDCore.getPlayer(source) + if not player then return end + + local ped = GetPlayerPed(source) + local coords = GetEntityCoords(ped) + local heading = GetEntityHeading(ped) + local info = NDCore.createVehicle({ + owner = player.id, + coords = coords, + heading = heading, + model = GetHashKey(args.model) + }) + + local veh = info.entity + for i=1, 10 do + if GetPedInVehicleSeat(veh, -1) ~= ped then + SetPedIntoVehicle(ped, veh, -1) + else + break + end + Wait(100) + end +end) From bbaf1aa90f4fce80e40f113a31cc3751d2af282e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:19:22 +0200 Subject: [PATCH 105/161] feat: revive player --- client/functions.lua | 39 +++++++++++++++++++++++++++++++++++++++ server/player.lua | 8 ++++++++ 2 files changed, 47 insertions(+) diff --git a/client/functions.lua b/client/functions.lua index 9cd34f2..074e0a5 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -26,6 +26,45 @@ function NDCore.getPlayersFromCoords(distance, coords) return closePlayers end +function NDCore.revivePlayer(reset, keepDead) + local usingAmbulance = GetResourceState("ND_Ambulance") == "started" + local veh = GetVehiclePedIsIn(cache.ped) + local seat = cache.seat + local coords = GetEntityCoords(cache.ped) + NetworkResurrectLocalPlayer(coords.x, coords.y, coords.z, GetEntityHeading(cache.ped), true, true, false) + + local ped = PlayerPedId() + if cache.ped ~= ped then + DeleteEntity(cache.ped) + ClearAreaOfPeds(coords.x, coords.y, coords.z, 1.5, false) + end + + SetEntityInvincible(ped, false) + FreezeEntityPosition(ped, false) + SetEntityVisible(ped, true) + SetEveryoneIgnorePlayer(ped, false) + SetPedCanBeTargetted(ped, true) + SetEntityCanBeDamaged(ped, true) + SetBlockingOfNonTemporaryEvents(ped, false) + SetPedCanRagdollFromPlayerImpact(ped, true) + ClearPedTasksImmediately(ped) + + if veh and veh ~= 0 then + SetPedIntoVehicle(ped, veh, seat) + end + if not keepDead then + LocalPlayer.state.dead = false + end + if reset and GetPedMovementClipset(ped) == `move_m@injured` then + ClearEntityLastDamageEntity(ped) + SetPedMoveRateOverride(ped, 1.0) + ResetPedMovementClipset(ped, 0) + end + if reset and usingAmbulance then + exports["ND_Ambulance"]:resetBodyDamage() + end +end + function NDCore.notify(...) if GetResourceState("ModernHUD") == "started" then exports["ModernHUD"]:notify(...) diff --git a/server/player.lua b/server/player.lua index 8b0fa0e..ea2557b 100644 --- a/server/player.lua +++ b/server/player.lua @@ -261,6 +261,14 @@ local function createCharacterTable(info) return true end + function self.revive() + self.triggerEvent("ND:revivePlayer") + self.setMetadata({ + dead = nil, + deathInfo = nil, + }) + end + ---@param reason string function self.drop(reason) if not self.source then return end From 39e302c50fc24c72d01920d4ce2acdd5e7de67e5 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 24 Sep 2023 11:19:36 +0200 Subject: [PATCH 106/161] wip: death system --- client/death.lua | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ fxmanifest.lua | 1 + server/main.lua | 10 +++++++++ 3 files changed, 66 insertions(+) create mode 100644 client/death.lua diff --git a/client/death.lua b/client/death.lua new file mode 100644 index 0000000..475ed6e --- /dev/null +++ b/client/death.lua @@ -0,0 +1,55 @@ +local ambulance +local usingAmbulance = false +local alreadyEliminated = false + +NDCore.isResourceStarted("ND_Ambulance", function(started) + usingAmbulance = started + if not usingAmbulance then return end + ambulance = exports["ND_Ambulance"] +end) + +local function PlayerEliminated(deathCause, killerServerId, killerClientId) + if alreadyEliminated then return end + alreadyEliminated = true + local info = { + deathCause = deathCause, + killerServerId = killerServerId, + killerClientId = killerClientId, + damagedBones = usingAmbulance and ambulance:getBodyDamage() or {} + } + TriggerEvent("ND:playerEliminated", info) + TriggerServerEvent("ND:playerEliminated", info) + Wait(1000) + alreadyEliminated = false +end + +AddEventHandler("gameEventTriggered", function(name, args) + if name ~= "CEventNetworkEntityDamage" then return end + + local victim = args[1] + if not IsPedAPlayer(victim) or NetworkGetPlayerIndexFromPed(victim) ~= cache.playerId then return end + + if not IsPedDeadOrDying(victim, true) and GetEntityHealth(victim) > 0 then + local hit, bone = GetPedLastDamageBone(victim) + if hit and usingAmbulance then + local damageWeapon = ambulance:getLastDamagingWeapon(victim) + return damageWeapon and ambulance:updateBodyDamage(bone, damageWeapon) + end + return + end + + local killerEntity, deathCause = GetPedSourceOfDeath(cache.ped), GetPedCauseOfDeath(cache.ped) + local killerClientId = NetworkGetPlayerIndexFromPed(killerEntity) + if killerEntity ~= cache.ped and killerClientId and NetworkIsPlayerActive(killerClientId) then + return PlayerEliminated(deathCause, GetPlayerServerId(killerClientId), killerClientId) + end + PlayerEliminated(deathCause) +end) + +local firstSpawn = true +exports.spawnmanager:setAutoSpawnCallback(function() + if firstSpawn then + firstSpawn = false + return exports.spawnmanager:spawnPlayer() and exports.spawnmanager:setAutoSpawn(false) + end +end) diff --git a/fxmanifest.lua b/fxmanifest.lua index 3199f27..e14996a 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -16,6 +16,7 @@ client_scripts { "client/peds.lua", "client/functions.lua", "client/events.lua", + "client/death.lua", "compatibility/**/client.lua" } server_scripts { diff --git a/server/main.lua b/server/main.lua index 3c8ead8..7ebb546 100644 --- a/server/main.lua +++ b/server/main.lua @@ -122,3 +122,13 @@ SetTimeout(500, function() "database/vehicles.sql" }, resourceName) end) + +RegisterNetEvent("ND:playerEliminated", function(info) + local src = source + local player = NDCore.getPlayer(src) + if not player then return end + player.setMetadata({ + dead = true, + deathInfo = info + }) +end) From 2696ae632d3fa34275e4f01f3a2f1063adb2961f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 25 Sep 2023 00:17:11 +0200 Subject: [PATCH 107/161] update(server/player): update job data for player --- server/player.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/player.lua b/server/player.lua index ea2557b..f01b3ef 100644 --- a/server/player.lua +++ b/server/player.lua @@ -336,7 +336,13 @@ local function createCharacterTable(info) ---@param rank number ---@return boolean function self.setJob(name, rank) - return self.addGroup(name, rank, true) + local job = self.addGroup(name, rank, true) + local jobName, jobInfo = self.getJob() + if jobInfo then + self.job = jobName + self.jobInfo = jobInfo + end + return job end ---@param job string From b72df8017956234b65df237a28f9620d87b3f60b Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 25 Sep 2023 00:17:52 +0200 Subject: [PATCH 108/161] wip(server/vehicle): giving keys access --- server/vehicle.lua | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 926c20b..16d750e 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -96,8 +96,29 @@ function NDCore.getVehicles(characterId) end function NDCore.giveVehicleAccess(source, vehicle, access, vehicleId, netId, plate, model, name, owner) - if inventoryStarted and Config.useInventoryForKeys and (not vehicleId or ox_inventory:GetItem(source, "keys", {vehId = vehicleId}, true) == 0) and (not netId or ox_inventory:GetItem(source, "keys", {vehNetId = netId}, true) == 0) then - if access then + if not vehicle or not DoesEntityExist(vehicle) then return end + local state = Entity(vehicle).state + + if not netId then + netId = NetworkGetNetworkIdFromEntity(vehicle) + end + if not vehicleId then + vehicleId = state.id or ("temp_%s%d"):format(string.char(math.random(65, 90)), math.random(1, 999999)) + end + if not plate then + plate = GetVehicleNumberPlateText(vehicle) + end + if not model then + model = GetEntityModel(vehicle) + end + if not state.id then + state.id = vehicleId + end + + if inventoryStarted and Config.useInventoryForKeys then + local item = ox_inventory:GetItem(source, "keys", {vehId = vehicleId, vehNetId = netId}, true) + local hasKey = item ~= 0 + if access and not hasKey then ox_inventory:AddItem(source, "keys", 1, { vehOwner = owner, vehId = vehicleId, @@ -106,12 +127,13 @@ function NDCore.giveVehicleAccess(source, vehicle, access, vehicleId, netId, pla keyEnabled = true, vehNetId = netId }) - else + elseif not access and hasKey then ox_inventory:RemoveItem(source, "keys", 1, { vehOwner = owner, vehId = vehicleId, vehPlate = plate, vehModel = name or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "", + keyEnabled = true, vehNetId = netId }) end @@ -120,7 +142,6 @@ function NDCore.giveVehicleAccess(source, vehicle, access, vehicleId, netId, pla local player = NDCore.getPlayer(source) if not player then return end - local state = Entity(vehicle).state if not state.keys then state.keys = { [player.id] = access @@ -446,7 +467,7 @@ exports("keys", function(event, item, inventory, slot, data) local metadata for i=1, #inventory.items do local item = inventory.items[i] - if item.slot == slot then + if item and item.slot == slot then metadata = item.metadata break end From 47b952be1d7aba63bb11b6feb6a01b7ee005f32f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 03:15:37 +0200 Subject: [PATCH 109/161] wip(server/vehicle): vehicle functions & management --- server/vehicle.lua | 222 +++++++++++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 98 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 16d750e..a65579e 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -36,46 +36,95 @@ local function generateVehiclePlate(newPlate) return plate end -local function createVehicleInfo(info) - local entity = info.entity +local function getVehicleDatabaseInfo(vehicle) + if not vehicle then return end + local stored = vehicle.stored == 1 + local impounded = vehicle.impounded == 1 + return { + id = vehicle.id, + owner = vehicle.owner, + plate = vehicle.plate, + properties = json.decode(vehicle.properties) or {} + stored = stored, + impounded = impounded, + stolen = vehicle.stolen == 1, + available = not impounded and not stored + } +end - function info.delete() +function NDCore.getVehicleById(vehicleId) + local result = MySQL.query.await("SELECT * FROM nd_vehicles WHERE id = ?", {vehicleId}) + return getVehicleDatabaseInfo(result?[1]) +end + +function NDCore.getVehicle(info) + local infoType = type(info) == "table" + local entity = infoType and info.entity or info + if not DoesEntityExist(entity) then return end + + if not infoType then + info = {} + end + + local state = Entity(entity).state + local self = { + entity = entity, + id = info.id or state.id, + owner = info.owner or state.owner, + keys = info.keys or state.keys, + properties = info.properties or state.props, + locked = info.locked or state.locked, + hotwired = info.hotwired or state.hotwired, + metadata = info.metadata or state.metadata, + netId = info.netId or NetworkGetNetworkIdFromEntity(entity) + } + + -- delete vehicle + function self.delete() if not DoesEntityExist(entity) then return end DeleteEntity(entity) end - function info.properties(props) + -- set vehicle properties + function self.setProperties(props) if not DoesEntityExist(entity) then return end + local properties = type(props) == "string" and json.decode(props) or props local state = Entity(entity).state - state.props = type(props) == "string" and json.decode(props) or props + state.props = properties + self.properties = properties + local id = self.id + if not id then return end + MySQL.query.await("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), id}) end - function info.plate(plate) + -- update vehicle plate + function self.plate(plate) if not DoesEntityExist(entity) or MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) then return end - SetVehicleNumberPlateText(entity, plate) + if self.properties then + self.properties.plate = plate + local state = Entity(entity).state + state.props = self.properties + end + + if not self.id or not self.owner then return end + MySQL.query("UPDATE nd_vehicles SET plate = ? WHERE id = ?", {plate, self.id}) return true end - return info -end + function self.store(delete) + if delete then + self.delete() + end + if not self.id or not self.owner then return end + MySQL.query.await("UPDATE nd_vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(self.properties), 1, self.id}) + -- player.triggerEvent("ND_Vehicles:returnVehicles", NDCore.getVehicles(player.id)) + end -function NDCore.getVehicle(entity) - if not DoesEntityExist(entity) then return end + if self.id then + playerOwnedVehicles[self.id] = self + end - local state = Entity(entity).state - local vehicleId = state.id - if not vehicleId then return end - - return createVehicleInfo({ - entity = entity, - owner = state.owner, - id = vehicleId, - properties = state.props, - locked = state.locked, - keys = state.keys, - hotwired = state.hotwired, - metadata = state.metadata - }) + return self end function NDCore.getVehicles(characterId) @@ -84,13 +133,10 @@ function NDCore.getVehicles(characterId) local vehicles = {} for _, vehicle in pairs(result) do - vehicles[#vehicles+1] = { - available = vehicle.stored == 1, - owner = vehicle.owner, - id = vehicle.id, - plate = vehicle.plate, - properties = json.decode(vehicle.properties) - } + local info = getVehicleDatabaseInfo(vehicle) + if info then + vehicles[#vehicles+1] = info + end end return vehicles end @@ -179,12 +225,7 @@ function NDCore.createVehicle(info) state.locked = true local keys = info.keys or {} - if vehicleId then - playerOwnedVehicles[vehicleId] = { - netid = netId, - entity = veh - } - else + if not vehicleId then vehicleId = ("temp_%s%d"):format(string.char(math.random(65, 90)), math.random(1, 999999)) end if not properties.plate then @@ -201,25 +242,30 @@ function NDCore.createVehicle(info) local vehicleName if inventoryStarted and Config.useInventoryForKeys then for charId, _ in pairs(keys) do - local player = NDCore.getPlayers("id", charId, true)[1] - local source = player and player.source - if source then + local playerSource = charId == owner and info.source + if not playerSource then + local player = NDCore.getPlayers("id", charId, true)[1] + playerSource = player and player.source + end + if playerSource then if not vehicleName then - vehicleName = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "" + vehicleName = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", playerSource, model) or "" end - NDCore.giveVehicleAccess(source, veh, true, vehicleId, netId, properties and properties.plate, model, vehicleName, owner) + NDCore.giveVehicleAccess(playerSource, veh, true, vehicleId, netId, properties and properties.plate, model, vehicleName, owner) end end end - - return createVehicleInfo({ + + return NDCore.getVehicle({ entity = veh, - netId = netId, - owner = owner, id = vehicleId, + owner = owner, + keys = keys properties = properties, locked = true, - keys = keys + hotwired = state.hotwired, + metadata = state.metadata + netId = netId, }) end @@ -228,8 +274,9 @@ function NDCore.transferVehicle(vehicleId, fromSource, toSource) local playerFrom = NDCore.getPlayer(fromSource) MySQL.query.await("UPDATE nd_vehicles SET owner = ? WHERE id = ?", {playerTo.id, vehicleId}) - if not playerOwnedVehicles[vehicleId] then return end - local veh = NetworkGetEntityFromNetworkId(playerOwnedVehicles[vehicleId].netid) + local info = playerOwnedVehicles[vehicleId] + if not info then return end + local veh = NetworkGetEntityFromNetworkId(info.netId) if not veh then playerFrom.notify({ title = "Ownership transfered", @@ -254,7 +301,7 @@ function NDCore.transferVehicle(vehicleId, fromSource, toSource) [playerTo.id] = true } - playerFrom.triggerEvent("ND_Vehicles:blip", playerOwnedVehicles[vehicleId].netid, false) + playerFrom.triggerEvent("ND_Vehicles:blip", info.netId, false) playerFrom.notify({ title = "Ownership transfered", @@ -314,56 +361,24 @@ function NDCore.giveVehicleKeys(vehicle, source, target) end function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) - local player = NDCore.getPlayer(source) + local player = NDCore.getPlayer(src) if not player then return end - local vehicles = NDCore.getVehicles(player.id) - for _, vehicle in pairs(vehicles) do - if vehicle.id == vehicleId and vehicle.owner == player.id then - MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) - vehicle.available = false - player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) - return NDCore.createVehicle({ - owner = vehicle.owner, - model = vehicle.properties.model, - coords = vec4(coords.x, coords.y, coords.z, coords.w or heading), - properties = vehicle.properties, - vehicleId = vehicleId, - source = source - }) - end - end -end -function NDCore.returnVehicleToGarage(source, veh, properties) - local player = NDCore.getPlayer(source) - if not DoesEntityExist(veh) then return end - - local vehID = Entity(veh).state.id - local vehicles = NDCore.getVehicles(player.id) + local vehicle = NDCore.getVehicleById(nil, vehicleId) + if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end - for _, vehicle in pairs(vehicles) do - if vehicle.owner == player.id and vehicle.id == vehID then - MySQL.query.await("UPDATE nd_vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(properties), 1, vehID}) - player.triggerEvent("ND_Vehicles:returnVehicles", NDCore.getVehicles(player.id)) - DeleteEntity(veh) - return true - end - end -end + vehicle.stored = false + MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) + player.triggerEvent("ND_Vehicles:returnVehicles", NDCore.getVehicles(player.id)) -function NDCore.saveVehicleProperties(source, veh, properties) - local player = NDCore.getPlayer(source) - if not DoesEntityExist(veh) then return end - - local vehID = Entity(veh).state.id - local vehicles = NDCore.getVehicles(player.id) - - for _, vehicle in pairs(vehicles) do - if vehicle.owner == player.id and vehicle.id == vehID then - MySQL.query.await("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), vehID}) - return true - end - end + return NDCore.createVehicle({ + owner = player.id, + model = vehicle.properties.model, + coords = vec4(coords.x, coords.y, coords.z, coords.w or coords.heading or heading), + properties = vehicle.properties, + vehicleId = vehicleId, + source = source + }) end RegisterNetEvent("ND_Vehicles:lockpick", function(netId, success) @@ -444,7 +459,7 @@ end local function lockNearestVehicle(source, vehId, metadata) local ped = GetPlayerPed(source) local pedCoords = GetEntityCoords(ped) - local veh = playerOwnedVehicles[vehId] and playerOwnedVehicles[vehId].entity + local veh = playerOwnedVehicles[vehId]?.entity if veh and DoesEntityExist(veh) then local vehCoords = GetEntityCoords(veh) @@ -553,3 +568,14 @@ RegisterNetEvent("ND_Vehicles:disableKey", function(slot) ox_inventory:SetMetadata(src, slot, key.metadata) end) +RegisterCommand("test", function(src, args, rawCommand) + local ped = GetPlayerPed(src) + local coords = GetEntityCoords(ped) + NDCore.spawnOwnedVehicle(src, 35, vec4(coords.x, coords.y, coords.z, GetEntityHeading(ped))) +end, false) + +RegisterCommand("test2", function(source, args, rawCommand) + local src = source + local properties = lib.callback.await("ND_Vehicles:getProps", src) + NDCore.setVehicleOwned(src, properties, true) +end, false) From 0848480753dfdc7d308e4203fe08d6c241e3cc8f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 03:16:02 +0200 Subject: [PATCH 110/161] refactor(database/vehicles): vehicle availability --- database/vehicles.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/database/vehicles.sql b/database/vehicles.sql index 4448a8e..df585cb 100644 --- a/database/vehicles.sql +++ b/database/vehicles.sql @@ -5,7 +5,9 @@ CREATE TABLE IF NOT EXISTS `nd_vehicles` ( `glovebox` LONGTEXT DEFAULT '[]', `trunk` LONGTEXT DEFAULT '[]', `properties` LONGTEXT DEFAULT '[]', - `stored` INT(11) DEFAULT NULL, + `stored` INT(11) DEFAULT '1', + `impounded` INT(11) DEFAULT '0', + `stolen` INT(11) DEFAULT '0', PRIMARY KEY (`id`) USING BTREE, INDEX `owner` (`owner`) USING BTREE, CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE From 7003b29db8d95754706145c809fb3c782c7fe022 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 03:16:33 +0200 Subject: [PATCH 111/161] fix(client/vehicle): setting props on as non entity owner --- client/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/vehicle.lua b/client/vehicle.lua index afb7da3..f42c6de 100644 --- a/client/vehicle.lua +++ b/client/vehicle.lua @@ -276,7 +276,7 @@ end) AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, replicated) local entity = GetEntityFromStateBagName(bagName) - if entity == 0 or not value then return end + if entity == 0 or not value or not NetworkGetEntityOwner(entity) == cache.playerId then return end lib.setVehicleProperties(entity, value) end) From dc7c34caada2bb4ae45824c731ffd42246033648 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 06:09:01 +0200 Subject: [PATCH 112/161] refactor(server/vehicle): functions & more --- server/vehicle.lua | 253 ++++++++++++++++++++++++--------------------- 1 file changed, 134 insertions(+), 119 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index a65579e..7825748 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -1,6 +1,6 @@ local ox_inventory local inventoryStarted = false -local playerOwnedVehicles = {} +local spawnedPlayerVehicles = {} NDCore.isResourceStarted("ox_inventory", function(started) inventoryStarted = started @@ -36,6 +36,10 @@ local function generateVehiclePlate(newPlate) return plate end +local function generateTemporaryVehicleId() + return ("temp_%s%d"):format(string.char(math.random(65, 90)), math.random(1, 999999)) +end + local function getVehicleDatabaseInfo(vehicle) if not vehicle then return end local stored = vehicle.stored == 1 @@ -44,7 +48,7 @@ local function getVehicleDatabaseInfo(vehicle) id = vehicle.id, owner = vehicle.owner, plate = vehicle.plate, - properties = json.decode(vehicle.properties) or {} + properties = json.decode(vehicle.properties) or {}, stored = stored, impounded = impounded, stolen = vehicle.stolen == 1, @@ -256,121 +260,116 @@ function NDCore.createVehicle(info) end end - return NDCore.getVehicle({ - entity = veh, - id = vehicleId, - owner = owner, - keys = keys - properties = properties, - locked = true, - hotwired = state.hotwired, - metadata = state.metadata - netId = netId, - }) + return NDCore.getVehicle(entity) end -function NDCore.transferVehicle(vehicleId, fromSource, toSource) - local playerTo = NDCore.getPlayer(toSource) +--- transfer ownership of a vehicle between players +---@param vehicleId number|string +---@param fromSource number +---@param toSource number +---@return boolean +function NDCore.transferVehicleOwnership(vehicleId, fromSource, toSource) local playerFrom = NDCore.getPlayer(fromSource) - MySQL.query.await("UPDATE nd_vehicles SET owner = ? WHERE id = ?", {playerTo.id, vehicleId}) - - local info = playerOwnedVehicles[vehicleId] - if not info then return end - local veh = NetworkGetEntityFromNetworkId(info.netId) - if not veh then - playerFrom.notify({ - title = "Ownership transfered", - description = "Vehicle ownership of has been transfered.", - type = "success", - position = "bottom-right", - duration = 4000 - }) - playerTo.notify({ - title = "Ownership received", - description = "Received vehicle ownership.", - type = "inform", - position = "bottom-right", - duration = 4000 - }) - return - end - - local state = Entity(veh).state - state.owner = playerTo.id - state.keys = { - [playerTo.id] = true - } + local playerTo = NDCore.getPlayer(toSource) + if not playerFrom or not playerTo then return end - playerFrom.triggerEvent("ND_Vehicles:blip", info.netId, false) + local vehicle = NDCore.getVehicleById(vehicleId) + if not vehicle or vehicle.owner ~= playerFrom.id then return end + + MySQL.query.await("UPDATE nd_vehicles SET owner = ? WHERE id = ?", {playerTo.id, vehicleId}) + local vehicleInfo = spawnedPlayerVehicles[vehicleId] + + if vehicleInfo then + local veh, netId, model = vehicleInfo.entity, vehicleInfo.netId, GetEntityModel(veh) + playerFrom.triggerEvent("ND_Vehicles:blip", netId, false) + playerTo.triggerEvent("ND_Vehicles:blip", netId, true) + + NDCore.giveVehicleAccess(fromSource, veh, false, { + vehicleId = vehicleId, + netId = netId, + model = model, + owner = playerFrom.id + }) + + local state = Entity(veh).state + state.owner = playerTo.id + NDCore.giveVehicleAccess(toSource, veh, true, { + vehicleId = vehicleId, + netId = netId, + model = model, + owner = playerTo.id + }) + end playerFrom.notify({ title = "Ownership transfered", - description = ("Vehicle ownership of %s has been transfered."):format(GetVehicleNumberPlateText(veh)), - type = "success", + description = ("Vehicle ownership of %s has been transfered."):format(vehicle.plate), position = "bottom-right", - duration = 4000 + type = "success" }) playerTo.notify({ title = "Ownership received", - description = ("Received vehicle ownership of %s."):format(GetVehicleNumberPlateText(veh)), - type = "inform", - position = "bottom-right", - duration = 4000 - }) -end - -function NDCore.setVehicleOwned(src, properties, stored) - local player = NDCore.getPlayer(src) - local plate = generateVehiclePlate() - properties.plate = plate - local id = MySQL.insert.await("INSERT INTO nd_vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {player.id, properties.plate, json.encode(properties), stored and 1 or 0}) - local vehicles = NDCore.getVehicles(player.id) - player.triggerEvent("ND_Vehicles:returnVehicles", vehicles) - return id -end - -function NDCore.giveVehicleKeys(vehicle, source, target) - local state = Entity(vehicle).state - if not state then return end - - local player = NDCore.getPlayer(source) - local owner = state.owner - if not owner and owner ~= player.id then return end - - local keys = state.keys - if not keys then return end - - local targetPlayer = NDCore.getPlayer(target) - state.keys[targetPlayer.id] = true - - player.notify({ - title = "Keys shared", - description = ("You've shared vehicle keys to %s."):format(GetVehicleNumberPlateText(vehicle)), - type = "success", - position = "bottom-right", - duration = 4000 - }) - targetPlayer.notify({ - title = "Keys received", - description = ("Received vehicle keys to %s."):format(GetVehicleNumberPlateText(vehicle)), - type = "inform", - position = "bottom-right", - duration = 4000 + description = ("Received vehicle ownership of %s."):format(vehicle.plate), + position = "bottom-right" }) return true end +--- set vehicle as owned by player +---@param playerId number +---@param properties table +---@param stored boolean +---@return vehicleId number +function NDCore.setVehicleOwned(playerId, properties, stored) + local plate = generateVehiclePlate() + properties.plate = plate + return MySQL.insert.await("INSERT INTO nd_vehicles (owner, plate, properties, stored) VALUES (?, ?, ?, ?)", {playerId, plate, json.encode(properties), stored and 1 or 0}) +end + +--- give a player access to another players vehicle as if they are next to eachother and hand the keys. +---@param source number +---@param target number +---@param vehicle number +---@return boolean +function NDCore.shareVehicleKeys(source, target, vehicle) + if not vehicle or not DoesEntityExist(vehicle) then return end + + local player = NDCore.getPlayer(source) + local targetPlayer = NDCore.getPlayer(target) + local state = Entity(vehicle).state + if not targetPlayer or not player or player.id ~= state.owner then return end + + NDCore.giveVehicleAccess(target, vehicle, true) + local plate = GetVehicleNumberPlateText(vehicle) + + player.notify({ + title = "Keys shared", + description = ("You've shared vehicle keys to %s."):format(plate), + position = "bottom-right", + type = "success", + }) + targetPlayer.notify({ + title = "Keys received", + description = ("Received vehicle keys to %s."):format(plate), + position = "bottom-right", + }) + return true +end + +--- spawned a vehicle that's owned by the player and checks for availability +---@param source number +---@param vehicleId number +---@param coords vector4 +---@return table function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) local player = NDCore.getPlayer(src) if not player then return end - local vehicle = NDCore.getVehicleById(nil, vehicleId) + local vehicle = NDCore.getVehicleById(vehicleId) if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end vehicle.stored = false MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) - player.triggerEvent("ND_Vehicles:returnVehicles", NDCore.getVehicles(player.id)) - return NDCore.createVehicle({ owner = player.id, model = vehicle.properties.model, @@ -381,26 +380,7 @@ function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) }) end -RegisterNetEvent("ND_Vehicles:lockpick", function(netId, success) - local veh = NetworkGetEntityFromNetworkId(netId) - local owner = NetworkGetEntityOwner(veh) - TriggerClientEvent("ND_Vehicles:syncAlarm", owner, netId) - if not success then return end - local state = Entity(veh).state - state.locked = false -end) - -RegisterNetEvent("ND_Vehicles:hotwire", function(netId) - local src = source - local ped = GetPlayerPed(src) - local playerVeh = GetVehiclePedIsIn(ped) - local veh = NetworkGetEntityFromNetworkId(netId) - if not playerVeh or playerVeh == 0 or playerVeh ~= veh then return end - local state = Entity(veh).state - state.hotwired = true -end) - -local function toggleVehicleLock(source, veh, nearby, metadata) +local function toggleVehicleLock(source, entity, nearby, metadata) local player = NDCore.getPlayer(source) if metadata and not metadata.keyEnabled then @@ -421,10 +401,11 @@ local function toggleVehicleLock(source, veh, nearby, metadata) }) end - local state = Entity(veh).state - local locked = not state.locked - state.locked = locked - player.triggerEvent("ND_Vehicles:keyFob", NetworkGetNetworkIdFromEntity(veh)) + local vehicle = NDCore.getVehicle(entity) + local locked = not vehicle.locked + vehicle.setLocked(locked) + + player.triggerEvent("ND_Vehicles:keyFob", vehicle.netId) if locked then return player.notify({ title = "LOCKED", @@ -459,7 +440,7 @@ end local function lockNearestVehicle(source, vehId, metadata) local ped = GetPlayerPed(source) local pedCoords = GetEntityCoords(ped) - local veh = playerOwnedVehicles[vehId]?.entity + local veh = spawnedPlayerVehicles[vehId]?.entity if veh and DoesEntityExist(veh) then local vehCoords = GetEntityCoords(veh) @@ -477,6 +458,7 @@ local function lockNearestVehicle(source, vehId, metadata) end end +--- inventory keys using item. exports("keys", function(event, item, inventory, slot, data) if event ~= "usingItem" or not Config.useInventoryForKeys or not inventoryStarted then return end local metadata @@ -493,6 +475,7 @@ exports("keys", function(event, item, inventory, slot, data) return false end) +-- if using inventory and inventory keys players can spawn keys when in their vehicle with this command. RegisterCommand("getkeys", function(source, args, rawCommand) if not Config.useInventoryForKeys or not inventoryStarted then return end local veh = GetVehiclePedIsIn(GetPlayerPed(source)) @@ -568,6 +551,38 @@ RegisterNetEvent("ND_Vehicles:disableKey", function(slot) ox_inventory:SetMetadata(src, slot, key.metadata) end) +-- sync alarm when vehicle is lockpicked. +RegisterNetEvent("ND_Vehicles:lockpick", function(netId, success) + local src = source + local veh = NetworkGetEntityFromNetworkId(netId) + if not veh or not DoesEntityExist(veh) then return end + + local ped = GetPlayerPed(src) + local pedCoords = GetEntityCoords(ped) + local vehCoords = GetEntityCoords(veh) + if #(pedCoords-vehCoords) > 5.0 then return end + + local owner = NetworkGetEntityOwner(veh) + TriggerClientEvent("ND_Vehicles:syncAlarm", owner, netId) + + if not success then return end + local state = Entity(veh).state + state.locked = false +end) + +-- sync alarm if vehicle gets hotwired. +RegisterNetEvent("ND_Vehicles:hotwire", function(netId) + local src = source + local ped = GetPlayerPed(src) + local playerVeh = GetVehiclePedIsIn(ped) + local veh = NetworkGetEntityFromNetworkId(netId) + if not playerVeh or playerVeh == 0 or playerVeh ~= veh then return end + local state = Entity(veh).state + state.hotwired = true +end) + +-- lib.callback.await("ND_Vehicles:getProps", src) + RegisterCommand("test", function(src, args, rawCommand) local ped = GetPlayerPed(src) local coords = GetEntityCoords(ped) From fcc0ce4551182f581d9cb6782b5625c6c130ddb3 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 06:09:53 +0200 Subject: [PATCH 113/161] refactor(refactor): refactor --- server/vehicle.lua | 191 +++++++++++++++++++++++++-------------------- 1 file changed, 107 insertions(+), 84 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 7825748..57aa52b 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -56,53 +56,65 @@ local function getVehicleDatabaseInfo(vehicle) } end +--- get vehicle information queried from the database by the vehicle id +---@param vehicleId string | numb +---@return table | nil function NDCore.getVehicleById(vehicleId) local result = MySQL.query.await("SELECT * FROM nd_vehicles WHERE id = ?", {vehicleId}) return getVehicleDatabaseInfo(result?[1]) end -function NDCore.getVehicle(info) - local infoType = type(info) == "table" - local entity = infoType and info.entity or info +--- get vehicle information and functions +---@param entity number +---@return table +function NDCore.getVehicle(entity) if not DoesEntityExist(entity) then return end - if not infoType then - info = {} - end - local state = Entity(entity).state local self = { entity = entity, - id = info.id or state.id, - owner = info.owner or state.owner, - keys = info.keys or state.keys, - properties = info.properties or state.props, - locked = info.locked or state.locked, - hotwired = info.hotwired or state.hotwired, - metadata = info.metadata or state.metadata, - netId = info.netId or NetworkGetNetworkIdFromEntity(entity) + id = state.id, + owner = state.owner, + keys = state.keys, + properties = state.props, + locked = state.locked, + hotwired = state.hotwired, + metadata = state.metadata, + netId = NetworkGetNetworkIdFromEntity(entity) } - -- delete vehicle + --- delete the vehicle function self.delete() if not DoesEntityExist(entity) then return end DeleteEntity(entity) end - -- set vehicle properties + --- set vehicle properties + ---@param props table function self.setProperties(props) if not DoesEntityExist(entity) then return end local properties = type(props) == "string" and json.decode(props) or props local state = Entity(entity).state state.props = properties self.properties = properties - local id = self.id - if not id then return end - MySQL.query.await("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), id}) + + if not self.id or not self.owner then return end + MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id}) end - -- update vehicle plate - function self.plate(plate) + --- set vehicle locked/unlocked + ---@param status boolean + function self.setLocked(status) + if not DoesEntityExist(entity) then return end + local state = Entity(entity).state + status.locked = status + self.locked = status + end + + --- update the vehicle plate + ---@param plate string + ---@return boolean + function self.setPlate(plate) if not DoesEntityExist(entity) or MySQL.scalar.await("SELECT 1 FROM nd_vehicles WHERE plate = ?", {plate}) then return end if self.properties then self.properties.plate = plate @@ -115,22 +127,27 @@ function NDCore.getVehicle(info) return true end - function self.store(delete) - if delete then - self.delete() - end + --- set the vehicle availability status + ---@param statusType string + ---@param status boolean + function self.setStatus(statusType, status) + if not lib.table.contains({"stored", "impounded", "stolen"}, statusType) then return end + if statusType ~= "stolen" then self.delete() end if not self.id or not self.owner then return end - MySQL.query.await("UPDATE nd_vehicles SET properties = ?, stored = ? WHERE id = ?", {json.encode(self.properties), 1, self.id}) - -- player.triggerEvent("ND_Vehicles:returnVehicles", NDCore.getVehicles(player.id)) + local query = ("UPDATE nd_vehicles %s = ? WHERE id = ?"):format(statusType) + MySQL.query(query, {status and 1 or 0, self.id}) end - if self.id then - playerOwnedVehicles[self.id] = self + if self.id and self.owner then + spawnedPlayerVehicles[self.id] = self end return self end +--- get a characters owned vehicles +---@param characterId number +---@return table function NDCore.getVehicles(characterId) local result = MySQL.query.await("SELECT * FROM nd_vehicles WHERE owner = ?", {characterId}) if not result then return {} end @@ -145,70 +162,68 @@ function NDCore.getVehicles(characterId) return vehicles end -function NDCore.giveVehicleAccess(source, vehicle, access, vehicleId, netId, plate, model, name, owner) +--- give a player keys/access to a vehicle +---@param source number +---@param vehicle number +---@param access boolean +---@param info table +function NDCore.giveVehicleAccess(source, vehicle, access, info) if not vehicle or not DoesEntityExist(vehicle) then return end local state = Entity(vehicle).state + local netId = info?.netId or NetworkGetNetworkIdFromEntity(vehicle) + local vehicleId = info?.vehicleId or state.id or generateTemporaryVehicleId() - if not netId then - netId = NetworkGetNetworkIdFromEntity(vehicle) - end - if not vehicleId then - vehicleId = state.id or ("temp_%s%d"):format(string.char(math.random(65, 90)), math.random(1, 999999)) - end - if not plate then - plate = GetVehicleNumberPlateText(vehicle) - end - if not model then - model = GetEntityModel(vehicle) - end if not state.id then state.id = vehicleId end - if inventoryStarted and Config.useInventoryForKeys then - local item = ox_inventory:GetItem(source, "keys", {vehId = vehicleId, vehNetId = netId}, true) - local hasKey = item ~= 0 - if access and not hasKey then - ox_inventory:AddItem(source, "keys", 1, { - vehOwner = owner, - vehId = vehicleId, - vehPlate = plate, - vehModel = name or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "", - keyEnabled = true, - vehNetId = netId - }) - elseif not access and hasKey then - ox_inventory:RemoveItem(source, "keys", 1, { - vehOwner = owner, - vehId = vehicleId, - vehPlate = plate, - vehModel = name or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "", - keyEnabled = true, - vehNetId = netId - }) - end - end - local player = NDCore.getPlayer(source) - if not player then return end - - if not state.keys then - state.keys = { - [player.id] = access - } - else - local keys = state.keys + if player then + local keys = state.keys or {} keys[player.id] = access state.keys = keys end + + if not inventoryStarted or not Config.useInventoryForKeys then return end + local plate = info?.plate or GetVehicleNumberPlateText(vehicle) + local model = info?.model or GetEntityModel(vehicle) + local modelName = info?.modelName or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "" + local hasKey = ox_inventory:GetSlotIdWithItem(source, "keys", { + vehId = vehicleId, + vehNetId = netId + }) + + if access and not hasKey then + ox_inventory:AddItem(source, "keys", 1, { + vehOwner = owner or state.owner, + vehId = vehicleId, + vehPlate = plate, + vehModel = modelName, + keyEnabled = true, + vehNetId = netId + }) + elseif not access and hasKey then + ox_inventory:RemoveItem(source, "keys", 1, { + vehOwner = owner or state.owner, + vehId = vehicleId, + vehPlate = plate, + vehModel = modelName, + keyEnabled = true, + vehNetId = netId + }) + end end +--- spawn a vehicle, set info & give keys. +---@param info table +---@return table function NDCore.createVehicle(info) local owner = info.owner - local vehicleId = info.vehicleId + local vehicleId = info.vehicleId or generateTemporaryVehicleId() local properties = info.properties or {} local coords = info.coords local spawnCoords = coords + local coordType = type(coords) if coordType == "vector3" or coordType == "table" then spawnCoords = vector4(coords.x, coords.y, coords.z, coords.w or coords.h or info.heading or 0.0) @@ -229,9 +244,6 @@ function NDCore.createVehicle(info) state.locked = true local keys = info.keys or {} - if not vehicleId then - vehicleId = ("temp_%s%d"):format(string.char(math.random(65, 90)), math.random(1, 999999)) - end if not properties.plate then properties.plate = generateVehiclePlate() end @@ -255,7 +267,14 @@ function NDCore.createVehicle(info) if not vehicleName then vehicleName = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", playerSource, model) or "" end - NDCore.giveVehicleAccess(playerSource, veh, true, vehicleId, netId, properties and properties.plate, model, vehicleName, owner) + NDCore.giveVehicleAccess(playerSource, veh, true, { + vehicleId = vehicleId, + netId = netId, + plate = properties?.plate, + model = model, + vehicleName = vehicleName, + owner = owner + }) end end end @@ -368,7 +387,6 @@ function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) local vehicle = NDCore.getVehicleById(vehicleId) if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end - vehicle.stored = false MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) return NDCore.createVehicle({ owner = player.id, @@ -492,10 +510,12 @@ RegisterCommand("getkeys", function(source, args, rawCommand) vehId = state.id, vehPlate = props.plate, vehModel = lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, props.model), - keyEnabled = true + keyEnabled = true, + vehNetId = NetworkGetNetworkIdFromEntity(veh) }) end, false) +-- key sharing if not using inventory or inventory keys. RegisterCommand("givekeys", function(source, args, rawCommand) if Config.useInventoryForKeys and inventoryStarted then return end @@ -510,15 +530,16 @@ RegisterCommand("givekeys", function(source, args, rawCommand) if veh == 0 then return end end - NDCore.giveVehicleKeys(veh, src, target) + NDCore.shareVehicleKeys(src, target, veh) end, false) +-- lock/unlock vehicles if they're within range. RegisterNetEvent("ND_Vehicles:toggleVehicleLock", function(netId) if Config.useInventoryForKeys and inventoryStarted then return end local src = source local veh = NetworkGetEntityFromNetworkId(netId) - if not DoesEntityExist(veh) then return end + if not veh or not DoesEntityExist(veh) then return end local ped = GetPlayerPed(src) local pedCoords = GetEntityCoords(ped) @@ -527,6 +548,7 @@ RegisterNetEvent("ND_Vehicles:toggleVehicleLock", function(netId) toggleVehicleLock(src, veh, #(pedCoords-vehCoords) < 25.0) end) +-- locking of npc vehicles, if the players spawns inside a vehicle it won't be locked. RegisterNetEvent("entityCreated", function(entity) if not DoesEntityExist(entity) or GetEntityType(entity) ~= 2 then return end local state = Entity(entity).state @@ -542,6 +564,7 @@ RegisterNetEvent("entityCreated", function(entity) state.locked = true end) +-- disables inventory vehicles keys, disabled vehicles keys can no longer be used. Kinda like taking the battery out. RegisterNetEvent("ND_Vehicles:disableKey", function(slot) local src = source local key = ox_inventory:GetSlot(src, slot) From 6ff461839de949b5c974b32a2383fbcc704f4efb Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 10:24:07 +0200 Subject: [PATCH 114/161] feat(vehicle): garages --- client/vehicle/data.lua | 243 +++++++++++++++++++++ client/vehicle/garages.lua | 265 +++++++++++++++++++++++ client/{vehicle.lua => vehicle/main.lua} | 17 +- fxmanifest.lua | 8 +- server/vehicle.lua | 88 ++++++-- 5 files changed, 594 insertions(+), 27 deletions(-) create mode 100644 client/vehicle/data.lua create mode 100644 client/vehicle/garages.lua rename client/{vehicle.lua => vehicle/main.lua} (97%) diff --git a/client/vehicle/data.lua b/client/vehicle/data.lua new file mode 100644 index 0000000..d16bc8a --- /dev/null +++ b/client/vehicle/data.lua @@ -0,0 +1,243 @@ +return { + { + garageType = "land", + groups = {"sahp", "lspd", "bcso"}, + ped = vector4(452.83, -1027.79, 28.54, 2.49), + vehicleSpawns = { + vector4(446.19, -1025.47, 28.24, 185.96), + vector4(438.75, -1026.09, 28.38, 184.29), + vector4(434.98, -1026.61, 28.46, 185.99), + vector4(431.26, -1027.31, 28.53, 185.59), + vector4(427.52, -1026.84, 28.58, 184.25) + } + }, + { + garageType = "land", + impound = true, + ped = vector4(407.99, -1624.74, 29.29, 229.93), + vehicleSpawns = { + vector4(396.34, -1644.28, 28.86, 319.10), + vector4(398.56, -1646.42, 28.8616, 319.18), + vector4(400.68, -1648.86, 28.86, 140.78), + vector4(403.32, -1650.54, 28.86, 319.92), + vector4(403.21, -1650.68, 28.86, 139.40) + } + }, + { + garageType = "plane", + ped = vector4(-941.05, -2966.04, 13.95, 136.06), + vehicleSpawns = { + vector4(-974.98, -2977.90, 14.55, 59.91) + } + }, + { + garageType = "heli", + ped = vector4(-731.00, -1394.54, 5.00, 245.52), + vehicleSpawns = { + vector4(-746.01, -1469.39, 5.68, 139.27), + vector4(-725.26, -1444.72, 5.68, 139.58) + } + }, + { + garageType = "plane", + ped = vector4(1742.77, 3298.25, 41.22, 132.91), + vehicleSpawns = { + vector4(1734.26, 3250.98, 41.96, 81.11), + vector4(1729.32, 3270.69, 41.74, 145.39) + } + }, + { + garageType = "plane", + ped = vector4(-1241.26, -3391.48, 13.94, 35.03), + vehicleSpawns = { + vector4(-1254.71, -3388.15, 14.54, 330.04), + vector4(-1270.36, -3378.80, 14.54, 330.20), + vector4(-1286.05, -3369.44, 14.54, 329.99) + } + }, + { + garageType = "plane", + ped = vector4(-1621.10, -3151.63, 13.99, 41.26), + vehicleSpawns = { + vector4(-1634.04, -3147.84, 14.60, 330.28), + vector4(-1649.75, -3138.34, 14.60, 329.20), + vector4(-1665.42, -3129.26, 14.60, 330.10) + } + }, + { + garageType = "heli", + ped = vector4(-1121.87, -2839.88, 13.95, 150.58), + vehicleSpawns = { + vector4(-1178.71, -2846.51, 14.62, 150.16), + vector4(-1146.40, -2865.22, 14.62, 151.08), + vector4(-1112.85, -2884.66, 14.62, 149.70) + } + }, + { + garageType = "water", + ped = vector4(-831.03, -1359.53, 5.00, 299.80), + vehicleSpawns = { + vector4(-846.16, -1362.07, 0.39, 110.54), + vector4(-842.53, -1372.00, 0.39, 111.54), + vector4(-849.29, -1353.26, 0.38, 109.53), + vector4(-852.26, -1345.09, 0.41, 110.52), + vector4(-855.49, -1336.60, 0.40, 107.43), + vector4(-858.63, -1328.30, 0.40, 109.82), + vector4(-839.35, -1380.30, 0.39, 109.27), + vector4(-836.38, -1388.93, 0.41, 110.11), + vector4(-833.29, -1397.32, 0.40, 110.80), + vector4(-830.34, -1405.65, 0.37, 110.16) + } + }, + { + garageType = "land", + ped = vector4(-280.32, -888.42, 31.32, 250.68), + vehicleSpawns = { + vector4(-282.36, -915.11, 30.38, 68.81), + vector4(-284.27, -918.37, 30.38, 70.58), + vector4(-285.44, -921.82, 30.38, 250.22), + vector4(-285.70, -887.62, 30.38, 167.04), + vector4(-292.70, -885.93, 30.38, 167.09), + vector4(-285.76, -887.47, 30.38, 169.59), + vector4(-300.44, -885.14, 30.38, 347.05), + vector4(-303.59, -883.71, 30.38, 167.69), + vector4(-309.38, -896.88, 30.38, 167.15), + vector4(-312.98, -896.30, 30.38, 166.46), + vector4(-316.68, -896.26, 30.37, 347.94), + vector4(-311.06, -881.94, 30.38, 168.21), + vector4(-314.78, -881.86, 30.37, 348.45) + } + }, + { + garageType = "land", + ped = vector4(597.53, 91.08, 93.13, 250.54), + vehicleSpawns = { + vector4(598.53, 98.37, 92.27, 69.47), + vector4(599.79, 102.00, 92.27, 249.38), + vector4(608.21, 103.90, 92.18, 248.98), + vector4(600.60, 111.38, 92.27, 73.07), + vector4(609.91, 107.59, 92.23, 68.77), + vector4(601.36, 114.99, 92.27, 250.19), + vector4(611.07, 111.39, 92.29, 249.69), + vector4(603.52, 118.52, 92.26, 67.62), + vector4(612.74, 114.94, 92.28, 69.01), + vector4(604.00, 122.58, 92.27, 249.76), + vector4(613.78, 118.83, 92.29, 248.82), + vector4(622.43, 115.48, 91.99, 70.39), + vector4(628.56, 110.25, 91.47, 249.30), + vector4(620.67, 111.89, 92.03, 250.24), + vector4(618.41, 104.51, 91.97, 72.22), + vector4(624.87, 99.25, 91.36, 63.32), + vector4(616.59, 100.82, 91.97, 249.00) + } + }, + { + garageType = "land", + ped = vector4(100.64, -1072.88, 29.37, 341.55), + vehicleSpawns = { + vector4(106.13, -1063.24, 28.51, 66.86), + vector4(107.82, -1059.73, 28.51, 66.99), + vector4(112.39, -1049.71, 28.52, 67.40), + vector4(110.76, -1053.09, 28.51, 68.13), + vector4(109.06, -1056.38, 28.51, 66.45), + vector4(117.53, -1081.03, 28.50, 181.40), + vector4(119.09, -1069.44, 28.50, 181.12), + vector4(121.22, -1081.42, 28.50, 0.57), + vector4(122.32, -1070.46, 28.50, 0.67), + vector4(125.62, -1069.54, 28.50, 179.25), + vector4(124.83, -1081.17, 28.50, 180.37), + vector4(128.62, -1081.71, 28.50, 1.36), + vector4(128.93, -1070.62, 28.50, 359.89), + vector4(132.24, -1069.35, 28.50, 181.54), + vector4(132.31, -1081.45, 28.50, 180.86), + vector4(135.93, -1081.35, 28.50, 358.82), + vector4(135.64, -1070.75, 28.50, 0.43), + vector4(139.75, -1081.91, 28.50, 182.18), + vector4(138.86, -1070.37, 28.50, 181.90), + vector4(143.55, -1081.41, 28.50, 0.89), + vector4(147.13, -1081.48, 28.50, 179.02), + vector4(150.95, -1081.49, 28.50, 358.79), + vector4(154.64, -1081.32, 28.50, 179.51), + vector4(158.38, -1082.03, 28.50, 1.57), + vector4(162.10, -1081.32, 28.50, 179.88) + } + }, + { + garageType = "land", + ped = vector4(214.84, -806.24, 30.81, 342.23), + vehicleSpawns = { + vector4(251.26, -774.63, 29.98, 67.34), + vector4(245.36, -772.34, 30.02, 66.81), + vector4(219.19, -765.93, 30.14, 249.78), + vector4(228.11, -768.94, 30.10, 249.66), + vector4(233.68, -771.18, 30.07, 249.82), + vector4(244.01, -775.06, 29.99, 249.41), + vector4(249.53, -777.07, 29.95, 251.09), + vector4(248.74, -779.52, 29.92, 69.14), + vector4(243.64, -777.63, 29.96, 69.23), + vector4(233.47, -773.91, 30.05, 69.88), + vector4(218.62, -768.58, 30.14, 69.81), + vector4(217.02, -770.83, 30.16, 248.11), + vector4(226.09, -773.95, 30.09, 249.30), + vector4(231.82, -776.14, 30.04, 249.17), + vector4(242.05, -779.91, 29.93, 246.99), + vector4(247.72, -782.02, 29.88, 251.04), + vector4(216.19, -773.65, 30.16, 67.90), + vector4(225.82, -776.85, 30.08, 65.64), + vector4(230.77, -778.89, 30.03, 69.40), + vector4(241.72, -782.84, 29.90, 69.13), + vector4(247.12, -784.93, 29.84, 67.56), + vector4(246.23, -787.13, 29.82, 247.56), + vector4(239.40, -784.68, 29.90, 248.06), + vector4(230.42, -781.41, 30.01, 247.48), + vector4(224.14, -779.01, 30.07, 247.39), + vector4(215.32, -775.95, 30.17, 248.99), + vector4(243.81, -792.19, 29.77, 246.23), + vector4(238.08, -789.95, 29.85, 250.62), + vector4(228.14, -786.30, 30.01, 249.07), + vector4(221.98, -783.92, 30.08, 248.25), + vector4(213.41, -781.01, 30.19, 249.21), + vector4(214.77, -778.70, 30.17, 67.86), + vector4(224.02, -781.91, 30.07, 68.82), + vector4(229.34, -784.05, 30.01, 66.57), + vector4(239.87, -787.83, 29.86, 69.37), + vector4(245.18, -789.87, 29.79, 68.00), + vector4(241.85, -797.20, 29.72, 245.51), + vector4(236.57, -795.07, 29.82, 247.54), + vector4(226.12, -791.34, 29.99, 247.84), + vector4(220.61, -789.19, 30.08, 248.09), + vector4(212.00, -786.22, 30.21, 248.93), + vector4(212.91, -783.71, 30.19, 68.61), + vector4(222.11, -786.89, 30.07, 70.10), + vector4(227.84, -789.16, 29.99, 67.55), + vector4(237.73, -792.73, 29.82, 69.40), + vector4(243.54, -794.88, 29.73, 68.74), + vector4(237.03, -812.64, 29.59, 243.63), + vector4(207.66, -798.70, 30.29, 70.06), + vector4(216.56, -801.92, 30.10, 70.31), + vector4(222.28, -804.17, 29.98, 69.97), + vector4(232.41, -807.95, 29.74, 69.42), + vector4(238.16, -810.17, 29.60, 68.95), + vector4(238.32, -807.43, 29.63, 248.28), + vector4(233.32, -805.44, 29.75, 248.13), + vector4(222.46, -801.41, 29.98, 248.50), + vector4(216.85, -799.11, 30.10, 248.54), + vector4(207.60, -795.90, 30.29, 248.64), + vector4(209.28, -793.74, 30.25, 69.82), + vector4(218.31, -796.92, 30.08, 68.55), + vector4(224.50, -799.34, 29.96, 68.63), + vector4(234.40, -803.02, 29.76, 66.67), + vector4(240.09, -805.27, 29.64, 69.02), + vector4(240.53, -802.44, 29.67, 248.95), + vector4(234.46, -800.13, 29.79, 249.40), + vector4(224.30, -796.51, 29.98, 248.31), + vector4(218.23, -794.00, 30.09, 249.09), + vector4(209.84, -791.10, 30.24, 251.56), + vector4(211.09, -788.68, 30.22, 69.53), + vector4(220.17, -791.73, 30.06, 70.56), + vector4(225.84, -794.12, 29.98, 67.53), + vector4(236.38, -797.85, 29.79, 69.39), + vector4(216.56, -801.90, 30.79, 69.39) + } + }, +} \ No newline at end of file diff --git a/client/vehicle/garages.lua b/client/vehicle/garages.lua new file mode 100644 index 0000000..68e9616 --- /dev/null +++ b/client/vehicle/garages.lua @@ -0,0 +1,265 @@ +local locations = require "client.vehicle.data" +local sprite = { + ["water"] = 356, + ["heli"] = 360, + ["plane"] = 359, + ["land"] = 357 +} +local garageTypes = { + ["water"] = 14, + ["heli"] = 15, + ["plane"] = 16 +} + +local clothing = { + { + face = { + drawable = 1, + texture = 1 + }, + undershirt = { + drawable = 0, + texture = 0 + }, + torso = { + drawable = 1, + texture = 1 + }, + leg = { + drawable = 0, + texture = 0 + }, + glasses = { + drawable = 1, + texture = 0 + }, + hat = { + drawable = -1, + texture = -1 + }, + }, + { + leg = { + drawable = 0, + texture = 1 + }, + undershirt = { + drawable = 0, + texture = 0 + }, + face = { + drawable = 0, + texture = 0 + }, + torso = { + drawable = 0, + texture = 2 + }, + glasses = { + drawable = -1, + texture = -1 + }, + hat = { + drawable = 0, + texture = 0 + }, + }, + { + face = { + drawable = 0, + texture = 2 + }, + undershirt = { + drawable = 0, + texture = 0 + }, + torso = { + drawable = 1, + texture = 2 + }, + leg = { + drawable = 0, + texture = 0 + }, + hat = { + drawable = -1, + texture = -1 + }, + glasses = { + drawable = -1, + texture = -1 + }, + } +} + +local function getClosestOwnedVehicle() + local coords = GetEntityCoords(cache.ped) + local vehicles = lib.getNearbyVehicles(coords, 50.0, true) + local nearestVeh = {} + + local function setNearestVehicle(veh) + local state = Entity(veh.vehicle).state + if not state.owner or state.owner ~= NDCore.player?.id then return end + + local nearestDist = nearestVeh.dist + local dist = #(coords-veh.coords) + if not nearestDist or dist < nearestDist then + nearestVeh.dist = dist + nearestVeh.coords = veh.coords + nearestVeh.entity = veh.vehicle + end + end + + for i=1, #vehicles do + setNearestVehicle(vehicles[i]) + end + return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist +end + +local function parkVehicle(veh) + if not veh or not DoesEntityExist(veh) then + return NDCore.notify({ + title = "Garage", + description = "No owned vehicle found nearby.", + type = "error", + position = "bottom", + duration = 3000 + }) + end + if GetPedInVehicleSeat(veh, -1) ~= 0 then + NDCore.notify({ + title = "Garage", + description = "Player in vehicle!", + type = "error", + position = "bottom", + duration = 3000 + }) + return + end + + local properties = lib.getVehicleProperties(veh) + properties.class = GetVehicleClass(veh) + TriggerServerEvent("ND_Vehicles:storeVehicle", VehToNet(veh)) +end + +local function isVehicleClassForGarage(class, garageType) + if not class then return true end + for garType, garClass in pairs(garageTypes) do + print(garType, garageType, garClass, class) + if garType == garageType and garClass == class then + return true + end + end +end + +local function getEngineStatus(health) + if health > 950 then + return "Perfect" + elseif health > 750 then + return "Good" + elseif health > 500 then + return "Bad" + end + return "Very bad" +end + +local function createMenuOptions(vehicle, vehicleSpawns) + local props = vehicle.properties + local makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model)) + local modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) + + local metadata = { + {label = "Plate", value = vehicle.properties.plate}, + {label = "Fuel", value = ("%d%s"):format(vehicle.properties.fuelLevel, "%")}, + {label = "Engine status", value = getEngineStatus(vehicle.properties.engineHealth)} + } + + if not makeName or makeName == "NULL" then + makeName = "" + else + metadata[#metadata+1] = {label = "Make", value = makeName} + makeName = makeName .. " " + end + if not modelName or modelName == "NULL" then + modelName = "" + else + metadata[#metadata+1] = {label = "Model", value = modelName} + end + + return { + title = ("%s%s"):format(makeName, modelName), + metadata = metadata, + onSelect = function(args) + TriggerServerEvent("ND_Vehicles:takeVehicle", vehicle.id, vehicleSpawns) + end, + } +end + +local function createMenu(vehicles, garageType, vehicleSpawns) + local options = { + { + title = "Park vehicle", + onSelect = function(args) + local veh = getClosestOwnedVehicle() + parkVehicle(veh) + end + } + } + for _, vehicle in ipairs(vehicles) do + if vehicle.available and isVehicleClassForGarage(vehicle.properties.class, garageType) then + options[#options+1] = createMenuOptions(vehicle, vehicleSpawns) + end + end + return { + id = ("garage_%s"):format(garageType), + title = "Parking garage", + options = options, + onExit = function() + garageOpen = false + end + } +end + +for i=1, #locations do + local location = locations[i] + NDCore.createAiPed({ + model = `s_m_y_airworker`, + coords = location.ped, + distance = 45.0, + clothing = clothing[math.random(1, #clothing)], + blip = { + label = ("Parking garage (%s)"):format(location.garageType), + sprite = sprite[location.garageType], + scale = 0.7, + color = 3 + }, + anim = { + dict = "anim@amb@casino@valet_scenario@pose_d@", + clip = "base_a_m_y_vinewood_01" + }, + options = { + { + name = "nd_core:garagePed", + icon = "fa-solid fa-warehouse", + label = "View garage", + distance = 2.0, + canInteract = function(entity, distance, coords, name, bone) + if not location.groups then return true end + local groups = location.groups + local playerGroups = NDCore.player?.groups + for i=1, #groups do + if playerGroups?[groups[i]] then + return true + end + end + end, + onSelect = function(data) + local vehicles = lib.callback.await("ND_Vehicles:getOwnedVehicles") or {} + local menu = createMenu(vehicles, location.garageType, location.vehicleSpawns) + lib.registerContext(menu) + lib.showContext(menu.id) + end + } + }, + }) +end diff --git a/client/vehicle.lua b/client/vehicle/main.lua similarity index 97% rename from client/vehicle.lua rename to client/vehicle/main.lua index f42c6de..b66d37a 100644 --- a/client/vehicle.lua +++ b/client/vehicle/main.lua @@ -241,14 +241,20 @@ local function getVehicleBlipSprite(entity) return typeBlip[model] or classBlip[class] or 225 -- 255 is default car blip end -RegisterNetEvent("ND_Vehicles:blip", function(netid, status) - local veh = NetToVeh(netid) +RegisterNetEvent("ND_Vehicles:blip", function(netId, status) + local time = GetCloudTimeAsInt() + while not NetworkDoesNetworkIdExist(netId) or not NetworkDoesEntityExistWithNetworkId(netId) and time-GetCloudTimeAsInt() < 5 do + Wait(100) + end + + local veh = NetToVeh(netId) if not veh then return end if not status then local blip = GetBlipFromEntity(veh) if not blip or not DoesBlipExist(blip) then return end return RemoveBlip(blip) end + local blip = AddBlipForEntity(veh) SetBlipSprite(blip, getVehicleBlipSprite(veh)) SetBlipColour(blip, 0) @@ -723,6 +729,13 @@ lib.onCache("ped", function(value) SetPedConfigFlag(value, 184, true) end) +lib.onCache("vehicle", function(value) + local veh = value or cache.vehicle + local blip = GetBlipFromEntity(veh) + if not blip or not DoesBlipExist(blip) then return end + SetBlipAlpha(blip, value and 0 or 255) +end) + SetTimeout(500, function() SetPedConfigFlag(cache.ped, 184, true) end) diff --git a/fxmanifest.lua b/fxmanifest.lua index e14996a..35cd0a8 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -12,8 +12,9 @@ shared_script "@ox_lib/init.lua" client_scripts { "client/main.lua", "shared/functions.lua", - "client/vehicle.lua", "client/peds.lua", + "client/vehicle/main.lua", + "client/vehicle/garages.lua", "client/functions.lua", "client/events.lua", "client/death.lua", @@ -30,6 +31,9 @@ server_scripts { "server/commands.lua" } -file "init.lua" +files { + "init.lua", + "client/vehicle/data.lua" +} dependency "oxmysql" diff --git a/server/vehicle.lua b/server/vehicle.lua index 57aa52b..861e7bc 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -52,7 +52,7 @@ local function getVehicleDatabaseInfo(vehicle) stored = stored, impounded = impounded, stolen = vehicle.stolen == 1, - available = not impounded and not stored + available = not impounded and stored } end @@ -84,8 +84,12 @@ function NDCore.getVehicle(entity) } --- delete the vehicle - function self.delete() + function self.delete(saveProperties) if not DoesEntityExist(entity) then return end + if saveProperties and self.id and self.owner then + local properties = lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity)) + MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id}) + end DeleteEntity(entity) end @@ -107,7 +111,7 @@ function NDCore.getVehicle(entity) function self.setLocked(status) if not DoesEntityExist(entity) then return end local state = Entity(entity).state - status.locked = status + state.locked = status self.locked = status end @@ -132,10 +136,11 @@ function NDCore.getVehicle(entity) ---@param status boolean function self.setStatus(statusType, status) if not lib.table.contains({"stored", "impounded", "stolen"}, statusType) then return end - if statusType ~= "stolen" then self.delete() end + if statusType ~= "stolen" then self.delete(true) end if not self.id or not self.owner then return end - local query = ("UPDATE nd_vehicles %s = ? WHERE id = ?"):format(statusType) + local query = ("UPDATE nd_vehicles SET %s = ? WHERE id = ?"):format(statusType) MySQL.query(query, {status and 1 or 0, self.id}) + return true end if self.id and self.owner then @@ -228,16 +233,22 @@ function NDCore.createVehicle(info) if coordType == "vector3" or coordType == "table" then spawnCoords = vector4(coords.x, coords.y, coords.z, coords.w or coords.h or info.heading or 0.0) end - if not spawnCoords then return end + if not spawnCoords then + return Citizen.Trace("NDCore.createVehicle", "spawnCoords not found") + end local model = info.model or properties.model local vehType = getVehicleType(model) - if not vehType then return end + if not vehType then + return Citizen.Trace("NDCore.createVehicle", "vehType not found") + end local veh = CreateVehicleServerSetter(model, vehType, spawnCoords.x, spawnCoords.y, spawnCoords.z, spawnCoords.w) local time = os.time() while not DoesEntityExist(veh) and time-os.time() < 5 do Wait(5) end - if not veh or not DoesEntityExist(veh) then return end + if not veh or not DoesEntityExist(veh) then + return Citizen.Trace("NDCore.createVehicle", "vehicle entity doesn't exist") + end local netId = NetworkGetNetworkIdFromEntity(veh) local state = Entity(veh).state @@ -279,7 +290,7 @@ function NDCore.createVehicle(info) end end - return NDCore.getVehicle(entity) + return NDCore.getVehicle(veh) end --- transfer ownership of a vehicle between players @@ -381,12 +392,12 @@ end ---@param coords vector4 ---@return table function NDCore.spawnOwnedVehicle(source, vehicleId, coords, heading) - local player = NDCore.getPlayer(src) + local player = NDCore.getPlayer(source) if not player then return end local vehicle = NDCore.getVehicleById(vehicleId) if not vehicle or not vehicle.available or vehicle.owner ~= player.id then return end - + MySQL.query.await("UPDATE nd_vehicles SET stored = ? WHERE id = ?", {0, vehicleId}) return NDCore.createVehicle({ owner = player.id, @@ -604,16 +615,47 @@ RegisterNetEvent("ND_Vehicles:hotwire", function(netId) state.hotwired = true end) --- lib.callback.await("ND_Vehicles:getProps", src) - -RegisterCommand("test", function(src, args, rawCommand) - local ped = GetPlayerPed(src) - local coords = GetEntityCoords(ped) - NDCore.spawnOwnedVehicle(src, 35, vec4(coords.x, coords.y, coords.z, GetEntityHeading(ped))) -end, false) - -RegisterCommand("test2", function(source, args, rawCommand) +RegisterNetEvent("ND_Vehicles:storeVehicle", function(netId) local src = source - local properties = lib.callback.await("ND_Vehicles:getProps", src) - NDCore.setVehicleOwned(src, properties, true) -end, false) + local vehicle = NDCore.getVehicle(NetworkGetEntityFromNetworkId(netId)) + if not vehicle then return end + + local player = NDCore.getPlayer(src) + if not vehicle.setStatus("stored", true) or not player or player.id ~= vehicle.owner then + return player.notify({ + title = "Garage", + description = "No owned vehicle found nearby.", + type = "error", + position = "bottom", + duration = 3000 + }) + end + player.notify({ + title = "Garage", + description = "Vehicle stored in garage.", + type = "success", + position = "bottom", + duration = 3000 + }) +end) + +local function isParkingAvailable(locations) + for i=1, #locations do + local loc = locations[math.random(1, #locations)] + if #getNearbyVehicles(vec3(loc.x, loc.y, loc.z), 2.0) == 0 then + return loc + end + end +end + +RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations) + local src = source + local info = NDCore.spawnOwnedVehicle(src, vehId, isParkingAvailable(locations)) + TriggerClientEvent("ND_Vehicles:blip", src, info.netId, true) +end) + +lib.callback.register("ND_Vehicles:getOwnedVehicles", function(src) + local player = NDCore.getPlayer(src) + if not player then return end + return NDCore.getVehicles(player.id) +end) From 4064d43bb04ca586c3b3c520d5a7af3796eab512 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:25:31 +0200 Subject: [PATCH 115/161] tweak(server/player): remove job group if job is set --- server/player.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/player.lua b/server/player.lua index f01b3ef..d3d3493 100644 --- a/server/player.lua +++ b/server/player.lua @@ -326,6 +326,7 @@ local function createCharacterTable(info) ---@param name string function self.removeGroup(name) local group = self.groups[name] + if not group then return end self.groups[name] = nil self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self)) lib.removePrincipal(self.source, ("group.%s"):format(name)) @@ -336,6 +337,7 @@ local function createCharacterTable(info) ---@param rank number ---@return boolean function self.setJob(name, rank) + self.removeGroup(self.job) local job = self.addGroup(name, rank, true) local jobName, jobInfo = self.getJob() if jobInfo then From 825ba96c50ca3c6962918cf82b4ba904c82fd16e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:26:04 +0200 Subject: [PATCH 116/161] feat: ped blip shown by group if added --- client/peds.lua | 49 +++++++++++++++++++++++++++++++++++++- client/vehicle/garages.lua | 3 ++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/client/peds.lua b/client/peds.lua index 7f7e08f..329ef46 100644 --- a/client/peds.lua +++ b/client/peds.lua @@ -1,5 +1,6 @@ local target, ox_target local locations = {} +local pedBlips = {} local clothingComponents = { face = 0, mask = 1, @@ -51,8 +52,28 @@ local function setClothing(ped, clothing) end end -local function createBlip(coords, info) +local function groupCheck(groups, playerGroups) + if not groups or #groups == 0 then return true end + for i=1, #groups do + if playerGroups and playerGroups[groups[i]] then + return true + end + end +end + +local function createBlip(coords, info, i) if not info then return end + local groups = info.groups + local playerGroups = NDCore.player?.groups + + local key = i or #pedBlips+1 + pedBlips[key] = { + groups = groups, + info = info, + coords = coords + } + + if not groupCheck(groups, playerGroups) then return end local blip = AddBlipForCoord(coords.x, coords.y, coords.z) SetBlipSprite(blip, info.sprite or 280) SetBlipScale(blip, info.scale or 0.8) @@ -63,9 +84,23 @@ local function createBlip(coords, info) AddTextComponentString(info.label) EndTextCommandSetBlipName(blip) end + + pedBlips[key].blip = blip return blip end +local function updateBlips(playerGroups) + for i=1, #pedBlips do + local blipInfo = pedBlips[i] + local access = groupCheck(blipInfo.groups, playerGroups) + if access and not blipInfo.blip or not DoesBlipExist(blipInfo.blip) then + createBlip(blipInfo.coords, blipInfo.info, i) + elseif not access and blipInfo.blip and DoesBlipExist(blipInfo.blip) then + RemoveBlip(blipInfo.blip) + end + end +end + function NDCore.createAiPed(info) local ped local model = type(info.model) == "string" and GetHashKey(info.model) or info.model @@ -148,6 +183,18 @@ function NDCore.removeAiPed(id) end end +RegisterNetEvent("ND:updateCharacter", function(character) + Wait(3000) + if character.id ~= NDCore.player?.id then return end + updateBlips(character.groups) +end) + +RegisterNetEvent("ND:characterLoaded", function(character) + Wait(3000) + if character.id ~= NDCore.player?.id then return end + updateBlips(character.groups) +end) + AddEventHandler("onResourceStop", function(name) if name == GetCurrentResourceName() then for i, _ in ipairs(locations) do diff --git a/client/vehicle/garages.lua b/client/vehicle/garages.lua index 68e9616..c0ade99 100644 --- a/client/vehicle/garages.lua +++ b/client/vehicle/garages.lua @@ -231,7 +231,8 @@ for i=1, #locations do label = ("Parking garage (%s)"):format(location.garageType), sprite = sprite[location.garageType], scale = 0.7, - color = 3 + color = 3, + groups = location.groups }, anim = { dict = "anim@amb@casino@valet_scenario@pose_d@", From bdb2c4033c44f7613b96d9186ec684096188fd92 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:58:41 +0200 Subject: [PATCH 117/161] fix(client/peds): model or entity not found --- client/peds.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/peds.lua b/client/peds.lua index 329ef46..a79aebc 100644 --- a/client/peds.lua +++ b/client/peds.lua @@ -124,14 +124,14 @@ function NDCore.createAiPed(info) } local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true) - lib.requestModel(model) + lib.requestModel(model, 500) function point:onEnter() ped = CreatePed(4, model, coords.x, coords.y, found and ground or coords.z, coords.w or coords.h or info.heading, false, false) - local timeOut = 100 - while not DoesEntityExist(ped) and timeOut > 0 do - Wait(10) - timeOut -= 1 + + local time = GetCloudTimeAsInt() + while not DoesEntityExist(ped) and time-GetCloudTimeAsInt() < 5 do + Wait(100) end configPed(ped) @@ -143,7 +143,7 @@ function NDCore.createAiPed(info) TaskPlayAnim(ped, anim.dict, anim.clip, 2.0, 8.0, -1, 1, 0, 0, 0, 0) end - if target and options then + if target and options and DoesEntityExist(ped) then ox_target:addLocalEntity({ped}, options) end end From 276274cf7424b0d59705084282925cc797b136b7 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:59:03 +0200 Subject: [PATCH 118/161] fix(vehicle/garage): properties not valid --- client/vehicle/garages.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/client/vehicle/garages.lua b/client/vehicle/garages.lua index c0ade99..110c65a 100644 --- a/client/vehicle/garages.lua +++ b/client/vehicle/garages.lua @@ -167,12 +167,17 @@ local function createMenuOptions(vehicle, vehicleSpawns) local props = vehicle.properties local makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model)) local modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) + local metadata = {} - local metadata = { - {label = "Plate", value = vehicle.properties.plate}, - {label = "Fuel", value = ("%d%s"):format(vehicle.properties.fuelLevel, "%")}, - {label = "Engine status", value = getEngineStatus(vehicle.properties.engineHealth)} - } + if vehicle.properties?.plate then + metadata[#metadata+1] = {label = "Plate", value = vehicle.properties.plate} + end + if vehicle.properties?.fuelLevel then + metadata[#metadata+1] = {label = "Fuel", value = ("%d%s"):format(vehicle.properties.fuelLevel, "%")} + end + if vehicle.properties?.engineHealth then + metadata[#metadata+1] = {label = "Engine status", value = getEngineStatus(vehicle.properties.engineHealth)} + end if not makeName or makeName == "NULL" then makeName = "" From 698707c992c8741f665a079fcaffb13fc939152f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:00:51 +0200 Subject: [PATCH 119/161] fix(vehicle): netId & properties inconsistencies --- client/vehicle/main.lua | 25 +++++++++++++++---------- server/vehicle.lua | 6 ++++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/client/vehicle/main.lua b/client/vehicle/main.lua index b66d37a..2d4287c 100644 --- a/client/vehicle/main.lua +++ b/client/vehicle/main.lua @@ -241,13 +241,16 @@ local function getVehicleBlipSprite(entity) return typeBlip[model] or classBlip[class] or 225 -- 255 is default car blip end -RegisterNetEvent("ND_Vehicles:blip", function(netId, status) +local function getVehFromNetId(netId) local time = GetCloudTimeAsInt() while not NetworkDoesNetworkIdExist(netId) or not NetworkDoesEntityExistWithNetworkId(netId) and time-GetCloudTimeAsInt() < 5 do Wait(100) end + return NetToVeh(netId) +end - local veh = NetToVeh(netId) +RegisterNetEvent("ND_Vehicles:blip", function(netId, status) + local veh = getVehFromNetId(netId) if not veh then return end if not status then local blip = GetBlipFromEntity(veh) @@ -265,16 +268,16 @@ RegisterNetEvent("ND_Vehicles:blip", function(netId, status) EndTextCommandSetBlipName(blip) end) -RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid) - local veh = NetToVeh(netid) +RegisterNetEvent("ND_Vehicles:syncAlarm", function(netId) + local veh = getVehFromNetId(netId) if not veh then return end SetVehicleAlarmTimeLeft(veh, 1) SetVehicleAlarm(veh, true) StartVehicleAlarm(veh) end) -RegisterNetEvent("ND_VehicleSystem:setOwnedIfNot", function(netid) - local veh = NetToVeh(netid) +RegisterNetEvent("ND_VehicleSystem:setOwnedIfNot", function(netId) + local veh = getVehFromNetId(netId) if not veh then return end setVehicleOwned(veh, true) setVehicleLocked(veh, true) @@ -312,8 +315,8 @@ local function playKeyFob(veh) return keyFob and DeleteEntity(keyFob) end -RegisterNetEvent("ND_Vehicles:keyFob", function(vehicleNetId) - playKeyFob(NetToVeh(vehicleNetId)) +RegisterNetEvent("ND_Vehicles:keyFob", function(netId) + playKeyFob(getVehFromNetId(netId)) end) local dontLock = { @@ -339,10 +342,12 @@ AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, end) end) -lib.callback.register("ND_Vehicles:getProps", function() - local veh = GetVehiclePedIsIn(cache.ped) +lib.callback.register("ND_Vehicles:getProps", function(netId) + local veh = getVehFromNetId(netId) local props = lib.getVehicleProperties(veh) local colorPrimary, colorSecondary = GetVehicleColours(veh) + if not props then return end + props.colorNamePrimary = vehicleColorNames[colorPrimary] props.colorNameSecondary = vehicleColorNames[colorSecondary] props.colorName = props.colorNamePrimary == props.colorNameSecondary and props.colorNamePrimary or ("%s & %s"):format(props.colorNamePrimary, props.colorNameSecondary) diff --git a/server/vehicle.lua b/server/vehicle.lua index 861e7bc..e4844b9 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -87,8 +87,10 @@ function NDCore.getVehicle(entity) function self.delete(saveProperties) if not DoesEntityExist(entity) then return end if saveProperties and self.id and self.owner then - local properties = lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity)) - MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id}) + local properties = lib.callback.await("ND_Vehicles:getProps", NetworkGetEntityOwner(entity), self.netId) + if properties then + MySQL.query("UPDATE nd_vehicles SET properties = ? WHERE id = ?", {json.encode(properties), self.id}) + end end DeleteEntity(entity) end From 0b40091e3eb599bcfef9e038f097c926d2f2d9fa Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:01:20 +0200 Subject: [PATCH 120/161] tweak(server/vehicle): removing keys on parking --- server/vehicle.lua | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index e4844b9..2cafc5d 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -210,14 +210,7 @@ function NDCore.giveVehicleAccess(source, vehicle, access, info) vehNetId = netId }) elseif not access and hasKey then - ox_inventory:RemoveItem(source, "keys", 1, { - vehOwner = owner or state.owner, - vehId = vehicleId, - vehPlate = plate, - vehModel = modelName, - keyEnabled = true, - vehNetId = netId - }) + ox_inventory:RemoveItem(source, "keys", 1, nil, hasKey) end end @@ -639,6 +632,11 @@ RegisterNetEvent("ND_Vehicles:storeVehicle", function(netId) position = "bottom", duration = 3000 }) + NDCore.giveVehicleAccess(src, vehicle.entity, false, { + vehicleId = vehicle.id, + netId = vehicle.netId, + owner = vehicle.owner + }) end) local function isParkingAvailable(locations) From 83212d45c2e2e0863e0d306889d289a518fea163 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 26 Sep 2023 23:47:24 +0200 Subject: [PATCH 121/161] fix(peds): inconsistency --- client/peds.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/peds.lua b/client/peds.lua index a79aebc..77c745d 100644 --- a/client/peds.lua +++ b/client/peds.lua @@ -123,10 +123,9 @@ function NDCore.createAiPed(info) resource = GetInvokingResource() } - local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true) - lib.requestModel(model, 500) - function point:onEnter() + local found, ground = GetGroundZFor_3dCoord(coords.x, coords.y, coords.z, true) + lib.requestModel(model) ped = CreatePed(4, model, coords.x, coords.y, found and ground or coords.z, coords.w or coords.h or info.heading, false, false) local time = GetCloudTimeAsInt() @@ -143,7 +142,8 @@ function NDCore.createAiPed(info) TaskPlayAnim(ped, anim.dict, anim.clip, 2.0, 8.0, -1, 1, 0, 0, 0, 0) end - if target and options and DoesEntityExist(ped) then + if target and options then + Wait(500) ox_target:addLocalEntity({ped}, options) end end From a5a9290b5d17e9ee5c146ee078f1d50bf7d601a6 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 27 Sep 2023 00:22:57 +0200 Subject: [PATCH 122/161] update(vehicle): impound check & reclaiming --- client/vehicle/garages.lua | 36 ++++++++++++++++++++++-------------- server/vehicle.lua | 28 ++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/client/vehicle/garages.lua b/client/vehicle/garages.lua index 110c65a..09c3536 100644 --- a/client/vehicle/garages.lua +++ b/client/vehicle/garages.lua @@ -142,11 +142,13 @@ local function parkVehicle(veh) TriggerServerEvent("ND_Vehicles:storeVehicle", VehToNet(veh)) end -local function isVehicleClassForGarage(class, garageType) - if not class then return true end +local function isVehicleAvailable(vehicle, garageType, impound) + local class = vehicle.properties.class + local available = vehicle.available and not impound or vehicle.impounded and impound + if available and not garageTypes[garageType] then return true end + for garType, garClass in pairs(garageTypes) do - print(garType, garageType, garClass, class) - if garType == garageType and garClass == class then + if available and garType == garageType and garClass == class then return true end end @@ -200,24 +202,30 @@ local function createMenuOptions(vehicle, vehicleSpawns) } end -local function createMenu(vehicles, garageType, vehicleSpawns) - local options = { - { +local function createMenu(vehicles, garageType, vehicleSpawns, impound) + local options = {} + if not impound then + options[#options+1] = { title = "Park vehicle", onSelect = function(args) local veh = getClosestOwnedVehicle() parkVehicle(veh) end } - } + end for _, vehicle in ipairs(vehicles) do - if vehicle.available and isVehicleClassForGarage(vehicle.properties.class, garageType) then + if isVehicleAvailable(vehicle, garageType, impound) then options[#options+1] = createMenuOptions(vehicle, vehicleSpawns) end end + if impound and #options == 0 then + options[#options+1] = { + title = "No vehicles found", + } + end return { id = ("garage_%s"):format(garageType), - title = "Parking garage", + title = impound and "Vehicle impound" or "Parking garage", options = options, onExit = function() garageOpen = false @@ -233,8 +241,8 @@ for i=1, #locations do distance = 45.0, clothing = clothing[math.random(1, #clothing)], blip = { - label = ("Parking garage (%s)"):format(location.garageType), - sprite = sprite[location.garageType], + label = location.impound and ("Impound (%s)"):format(location.garageType) or ("Parking garage (%s)"):format(location.garageType), + sprite = location.impound and 285 or sprite[location.garageType], scale = 0.7, color = 3, groups = location.groups @@ -247,7 +255,7 @@ for i=1, #locations do { name = "nd_core:garagePed", icon = "fa-solid fa-warehouse", - label = "View garage", + label = location.impound and "View impounded vehicles" or "View garage", distance = 2.0, canInteract = function(entity, distance, coords, name, bone) if not location.groups then return true end @@ -261,7 +269,7 @@ for i=1, #locations do end, onSelect = function(data) local vehicles = lib.callback.await("ND_Vehicles:getOwnedVehicles") or {} - local menu = createMenu(vehicles, location.garageType, location.vehicleSpawns) + local menu = createMenu(vehicles, location.garageType, location.vehicleSpawns, location.impound) lib.registerContext(menu) lib.showContext(menu.id) end diff --git a/server/vehicle.lua b/server/vehicle.lua index 2cafc5d..c932c7f 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -199,7 +199,7 @@ function NDCore.giveVehicleAccess(source, vehicle, access, info) vehId = vehicleId, vehNetId = netId }) - + if access and not hasKey then ox_inventory:AddItem(source, "keys", 1, { vehOwner = owner or state.owner, @@ -650,7 +650,31 @@ end RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations) local src = source - local info = NDCore.spawnOwnedVehicle(src, vehId, isParkingAvailable(locations)) + local vehicle = NDCore.getVehicleById(vehId) + local player = NDCore.getPlayer(src) + if not player or not vehicle or vehicle.owner ~= player.id then return end + if vehicle.impounded then + local reclaimPrice = 200 + if player.deductMoney("bank", reclaimPrice, "Vehicle impound reclaim") then + MySQL.query("UPDATE nd_vehicles SET impounded = ? WHERE id = ?", {0, vehicle.id}) + player.notify({ + title = "Impound", + description = ("Paid $%d to reclaim vehicle!"):format(reclaimPrice), + type = "success", + position = "bottom" + }) + else + player.notify({ + title = "Impound", + description = ("Price to reclaim is $%d, you don't have enough!"):format(reclaimPrice), + type = "error", + position = "bottom" + }) + end + end + + local info = NDCore.spawnOwnedVehicle(src, vehicle.id, isParkingAvailable(locations)) + if not info then return end TriggerClientEvent("ND_Vehicles:blip", src, info.netId, true) end) From 2a7b21f3ea9ea948b8785889e4d5bcc800dcf599 Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 27 Sep 2023 04:56:43 +0200 Subject: [PATCH 123/161] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 209e72e..b39e222 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,11 @@ * [oxmysql](https://github.com/overextended/oxmysql/releases) * [ox_lib](https://github.com/overextended/ox_lib/releases) -## Addons -* [Character Selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2) +## v2 Addons +* [Character selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2) +* [Banking](https://github.com/ND-Framework/ND_Banking/tree/wip-v2) +* [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/tree/wip-v2) +* [Inventory](https://github.com/overextended/ox_inventory/pull/1403) # Need support? [](https://discord.gg/Z9Mxu72zZ6) From f0eb040ebad357a02425e948b54904787d39a741 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:48:02 +0200 Subject: [PATCH 124/161] tweak(client/vehicle/garage): readonly menu button --- client/vehicle/garages.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/client/vehicle/garages.lua b/client/vehicle/garages.lua index 09c3536..3dfb3e9 100644 --- a/client/vehicle/garages.lua +++ b/client/vehicle/garages.lua @@ -221,6 +221,7 @@ local function createMenu(vehicles, garageType, vehicleSpawns, impound) if impound and #options == 0 then options[#options+1] = { title = "No vehicles found", + readOnly = true } end return { From c057455cc9a8cf939a1d9f63eae178e4f50f227f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:48:11 +0200 Subject: [PATCH 125/161] Update peds.lua --- client/peds.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/client/peds.lua b/client/peds.lua index 77c745d..7a4a16f 100644 --- a/client/peds.lua +++ b/client/peds.lua @@ -178,7 +178,6 @@ function NDCore.removeAiPed(id) if info.options then ox_target:removeLocalEntity({ped}) end - Wait(500) DeleteEntity(ped) end end From fb7c7b1de49357c41ff82a5572b271d453a71df7 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:48:18 +0200 Subject: [PATCH 126/161] Update vehicle.lua --- server/vehicle.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index c932c7f..5ea3097 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -663,8 +663,9 @@ RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations) type = "success", position = "bottom" }) + Wait(500) else - player.notify({ + return player.notify({ title = "Impound", description = ("Price to reclaim is $%d, you don't have enough!"):format(reclaimPrice), type = "error", From b8bb98b20d10f631dd008cf4eac476377b0077e1 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 14 Oct 2023 20:04:39 +0200 Subject: [PATCH 127/161] fix(vehicle): removing old keys & check for engine keys --- client/vehicle/main.lua | 2 +- server/vehicle.lua | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/vehicle/main.lua b/client/vehicle/main.lua index 2d4287c..82814a1 100644 --- a/client/vehicle/main.lua +++ b/client/vehicle/main.lua @@ -372,7 +372,7 @@ local function hasVehicleKeys(veh, checkEngine) local state = Entity(veh).state if Config.ox_inventory and Config.useInventoryForKeys then local metadata = { - vehPlate = GetVehicleNumberPlateText(veh), + vehId = state.id, keyEnabled = true } local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0 diff --git a/server/vehicle.lua b/server/vehicle.lua index 5ea3097..dbf162c 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -196,8 +196,7 @@ function NDCore.giveVehicleAccess(source, vehicle, access, info) local model = info?.model or GetEntityModel(vehicle) local modelName = info?.modelName or model and lib.callback.await("ND_Vehicles:getVehicleModelMakeLabel", source, model) or "" local hasKey = ox_inventory:GetSlotIdWithItem(source, "keys", { - vehId = vehicleId, - vehNetId = netId + vehId = vehicleId }) if access and not hasKey then From a3279ea5c81c6ea5dc3a0da765c1564aaa7fad82 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:28:36 +0200 Subject: [PATCH 128/161] feat(server): set character if not using multi character --- server/functions.lua | 4 ++++ server/main.lua | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/server/functions.lua b/server/functions.lua index fd0b19b..f2aafbd 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -88,6 +88,10 @@ function NDCore.getDiscordInfo(discordUserId) return data end +function NDCore.enableMultiCharacter(enable) + Config.multiCharacter = enable +end + for name, func in pairs(NDCore) do if type(func) == "function" then exports(name, func) diff --git a/server/main.lua b/server/main.lua index 7ebb546..e0c45f8 100644 --- a/server/main.lua +++ b/server/main.lua @@ -21,7 +21,8 @@ Config = { disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, groups = json.decode(GetConvar("core:groups", "[]")), - admins = json.decode(GetConvar("core:admins", "[]")) + admins = json.decode(GetConvar("core:admins", "[]")), + multiCharacter = false } SetConvarServerInfo("Discord", Config.discordInvite) @@ -45,6 +46,23 @@ AddEventHandler("playerJoining", function(oldId) local src = source PlayersInfo[src] = tempPlayersInfo[oldId] tempPlayersInfo[oldId] = nil + + if Config.multiCharacter then return end + Wait(3000) + + local characters = NDCore.fetchAllCharacters(src) + local id = next(characters) + if id then + return NDCore.setActiveCharacter(src, id) + end + + local player = NDCore.newCharacter(src, { + firstname = GetPlayerName(src), + lastname = "", + dob = "", + gender = "" + }) + NDCore.setActiveCharacter(src, player.id) end) local function checkDiscordIdentifier(identifiers) From 0fa2ee4d4b3e57b49b93dd8bed538fbdeca9338e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 21 Oct 2023 21:44:17 +0200 Subject: [PATCH 129/161] feat: core admins trough discord roles --- README.md | 7 ++++--- server/main.lua | 1 + server/player.lua | 11 +++++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b39e222..81cad49 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,11 @@ setr core:disableVehicleAirControl true setr core:useInventoryForKeys true # You can set admins here by their identifiers, admins will receive admin group in core and have access to group.admin ace perms. -# Admins get access to commands and more. -set core:admins ["fivem:1459624"] +# You can also use Discord role ids, Admins get access to commands and more. +set core:admins ["fivem:1459624", "fivem:1152629"] +set core:adminDiscordRoles ["944284542758449212", "93422454258349612", "93345451558145232"] -# Allow ox_lib to use commands, don't remove this. +# Allow ox_lib to use commands, don't remove this! add_ace resource.ox_lib command.add_ace allow add_ace resource.ox_lib command.remove_ace allow add_ace resource.ox_lib command.add_principal allow diff --git a/server/main.lua b/server/main.lua index e0c45f8..c692d6b 100644 --- a/server/main.lua +++ b/server/main.lua @@ -22,6 +22,7 @@ Config = { useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, groups = json.decode(GetConvar("core:groups", "[]")), admins = json.decode(GetConvar("core:admins", "[]")), + adminDiscordRoles = json.decode(GetConvar("core:adminDiscordRoles", "[]")), multiCharacter = false } diff --git a/server/player.lua b/server/player.lua index d3d3493..652f3b6 100644 --- a/server/player.lua +++ b/server/player.lua @@ -285,6 +285,17 @@ local function createCharacterTable(info) self.addGroup("admin") end end + + local roles = self.discord.roles + if roles then + for i=1, #Config.adminDiscordRoles do + local role = Config.adminDiscordRoles[i] + if lib.table.contains(roles, role) then + self.addGroup("admin") + end + end + end + for name, _ in pairs(self.groups) do lib.addPrincipal(self.source, ("group.%s"):format(name)) end From d341d8aaa5518753f51764a4b236d1b54fb4142c Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 23 Oct 2023 02:54:54 +0200 Subject: [PATCH 130/161] Update vehicle.lua --- server/vehicle.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index dbf162c..c81c1ab 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -255,9 +255,9 @@ function NDCore.createVehicle(info) if owner then keys[owner] = true state.owner = owner - state.keys = keys end + state.keys = keys state.props = properties state.id = vehicleId local vehicleName From a6db224699d0b3897da005625c3c71c959df31d4 Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 23 Oct 2023 19:51:54 +0200 Subject: [PATCH 131/161] Update commands.lua --- server/commands.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/commands.lua b/server/commands.lua index 0f0b066..8969728 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -284,7 +284,7 @@ lib.addCommand("revive", { end) lib.addCommand("dv", { - help = "Admin command, revive player.", + help = "Admin command, delete vehicles within the range.", restricted = "group.admin", params = { { From 87cb479ce453010d3d98a149c2533af92a5d1d2e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 25 Oct 2023 21:17:37 +0200 Subject: [PATCH 132/161] fix: bot token check --- server/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/functions.lua b/server/functions.lua index f2aafbd..77dda58 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -57,6 +57,7 @@ function NDCore.loadSQL(fileLocation, resource) end function NDCore.getDiscordInfo(discordUserId) + if not discordUserId or not Config.discordBotToken or not Config.discordGuildId then return end local done = false local data local discordErrors = { @@ -66,7 +67,6 @@ function NDCore.getDiscordInfo(discordUserId) [429] = "Discord bot rate limited" } - if not discordUserId then return end if type(discordUserId) == "string" and discordUserId:find("discord:") then discordUserId:gsub("discord:", "") end PerformHttpRequest(("https://discordapp.com/api/guilds/%s/members/%s"):format(Config.discordGuildId, discordUserId), function(errorCode, resultData, resultHeaders) From 40e74f69440a833428356aff0638e0127fc95f77 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Wed, 25 Oct 2023 21:47:43 +0200 Subject: [PATCH 133/161] fix: allow multiple callbacks of a resource --- shared/functions.lua | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/shared/functions.lua b/shared/functions.lua index 4bc42da..a19c4cf 100644 --- a/shared/functions.lua +++ b/shared/functions.lua @@ -1,25 +1,29 @@ local startedResources = {} -local callbacks = {} + +local function stateChanged(resourceName, state) + local callbacks = startedResources[resourceName] + if not callbacks then return end + for _, cb do + cb(state) + end +end AddEventHandler("onResourceStart", function(resourceName) - startedResources[resourceName] = true - local callback = callbacks[resourceName] - if not callback then return end - callback(true) + stateChanged(resourceName, true) end) AddEventHandler("onResourceStop", function(resourceName) - startedResources[resourceName] = nil - local callback = callbacks[resourceName] - if not callback then return end - callback(false) + stateChanged(resourceName, false) end) function NDCore.isResourceStarted(resourceName, cb) local started = GetResourceState(resourceName) == "started" if cb then - startedResources[resourceName] = started - callbacks[resourceName] = cb + if not startedResources[resourceName] then + startedResources[resourceName] = {} + end + local invokingResource = GetInvokingResource() + startedResources[resourceName][invokingResource] = cb cb(started) end return started From b5c7d4dc0b1dcefe73171e79a4e08ced5b3d8931 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:53:21 +0200 Subject: [PATCH 134/161] fix(shared/functions): isreosurcestarted errors --- shared/functions.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/functions.lua b/shared/functions.lua index a19c4cf..2464b0e 100644 --- a/shared/functions.lua +++ b/shared/functions.lua @@ -3,7 +3,8 @@ local startedResources = {} local function stateChanged(resourceName, state) local callbacks = startedResources[resourceName] if not callbacks then return end - for _, cb do + for i=1, #callbacks do + local cb = callbacks[i] cb(state) end end @@ -22,8 +23,7 @@ function NDCore.isResourceStarted(resourceName, cb) if not startedResources[resourceName] then startedResources[resourceName] = {} end - local invokingResource = GetInvokingResource() - startedResources[resourceName][invokingResource] = cb + startedResources[resourceName][#startedResources[resourceName]+1] = cb cb(started) end return started From 8aee90a10fcf19e3f3acfc549aa76dda5ca1ecc2 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 29 Oct 2023 10:20:55 +0100 Subject: [PATCH 135/161] update(client/garages): context menu --- client/vehicle/garages.lua | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/client/vehicle/garages.lua b/client/vehicle/garages.lua index 3dfb3e9..2cce0e9 100644 --- a/client/vehicle/garages.lua +++ b/client/vehicle/garages.lua @@ -171,16 +171,6 @@ local function createMenuOptions(vehicle, vehicleSpawns) local modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) local metadata = {} - if vehicle.properties?.plate then - metadata[#metadata+1] = {label = "Plate", value = vehicle.properties.plate} - end - if vehicle.properties?.fuelLevel then - metadata[#metadata+1] = {label = "Fuel", value = ("%d%s"):format(vehicle.properties.fuelLevel, "%")} - end - if vehicle.properties?.engineHealth then - metadata[#metadata+1] = {label = "Engine status", value = getEngineStatus(vehicle.properties.engineHealth)} - end - if not makeName or makeName == "NULL" then makeName = "" else @@ -193,8 +183,29 @@ local function createMenuOptions(vehicle, vehicleSpawns) metadata[#metadata+1] = {label = "Model", value = modelName} end + if props?.plate then + metadata[#metadata+1] = {label = "Plate", value = props.plate} + end + if props?.engineHealth then + metadata[#metadata+1] = { + label = "Engine status", + value = getEngineStatus(props.engineHealth), + progress = props.engineHealth/10, + colorScheme = "blue" + } + end + + if props?.fuelLevel then + metadata[#metadata+1] = { + label = "Fuel", + value = ("%d%s"):format(props.fuelLevel, "%"), + progress = props.fuelLevel, + colorScheme = "yellow" + } + end + return { - title = ("%s%s"):format(makeName, modelName), + title = ("Vehicle: %s%s\nPlate: %s"):format(makeName, modelName, props?.plate or "not found"), metadata = metadata, onSelect = function(args) TriggerServerEvent("ND_Vehicles:takeVehicle", vehicle.id, vehicleSpawns) From cebe404d42672748d8b398e3fa009e158506b41a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 29 Oct 2023 11:20:33 +0100 Subject: [PATCH 136/161] feat: vehicle metadata --- database/vehicles.sql | 1 + server/vehicle.lua | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/database/vehicles.sql b/database/vehicles.sql index df585cb..c4ae6dd 100644 --- a/database/vehicles.sql +++ b/database/vehicles.sql @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS `nd_vehicles` ( `stored` INT(11) DEFAULT '1', `impounded` INT(11) DEFAULT '0', `stolen` INT(11) DEFAULT '0', + `metadata` LONGTEXT DEFAULT '[]', PRIMARY KEY (`id`) USING BTREE, INDEX `owner` (`owner`) USING BTREE, CONSTRAINT `vehowner` FOREIGN KEY (`owner`) REFERENCES `nd_characters` (`charid`) ON UPDATE CASCADE ON DELETE CASCADE diff --git a/server/vehicle.lua b/server/vehicle.lua index c81c1ab..2f89ec0 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -52,7 +52,8 @@ local function getVehicleDatabaseInfo(vehicle) stored = stored, impounded = impounded, stolen = vehicle.stolen == 1, - available = not impounded and stored + available = not impounded and stored, + metadata = json.decode(vehicle.metadata) or {} } end @@ -79,7 +80,7 @@ function NDCore.getVehicle(entity) properties = state.props, locked = state.locked, hotwired = state.hotwired, - metadata = state.metadata, + metadata = state.metadata or {}, netId = NetworkGetNetworkIdFromEntity(entity) } @@ -145,6 +146,18 @@ function NDCore.getVehicle(entity) return true end + function self.setMetadata(key, value) + self.metadata[key] = value + if DoesEntityExist(entity) then + local state = Entity(self.entity).state + local metadata = state.metadata + metadata[key] = value + state.metadata = metadata + end + if not self.id or not self.owner then return end + MySQL.query("UPDATE nd_vehicles SET metadata = ? WHERE id = ?", {json.encode(self.metadata), self.id}) + end + if self.id and self.owner then spawnedPlayerVehicles[self.id] = self end From a8c5ea7d798904f4c2d95616e5433505fc1fd71e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 29 Oct 2023 11:23:05 +0100 Subject: [PATCH 137/161] tweak(server/vehicle): reclaiming impounded veh --- server/vehicle.lua | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/server/vehicle.lua b/server/vehicle.lua index 2f89ec0..85f9fd1 100644 --- a/server/vehicle.lua +++ b/server/vehicle.lua @@ -666,17 +666,8 @@ RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations) local player = NDCore.getPlayer(src) if not player or not vehicle or vehicle.owner ~= player.id then return end if vehicle.impounded then - local reclaimPrice = 200 - if player.deductMoney("bank", reclaimPrice, "Vehicle impound reclaim") then - MySQL.query("UPDATE nd_vehicles SET impounded = ? WHERE id = ?", {0, vehicle.id}) - player.notify({ - title = "Impound", - description = ("Paid $%d to reclaim vehicle!"):format(reclaimPrice), - type = "success", - position = "bottom" - }) - Wait(500) - else + local reclaimPrice = vehicle.metadata.impoundReclaimPrice or 200 + if not player.deductMoney("bank", reclaimPrice, "Vehicle impound reclaim") then return player.notify({ title = "Impound", description = ("Price to reclaim is $%d, you don't have enough!"):format(reclaimPrice), @@ -684,6 +675,13 @@ RegisterNetEvent("ND_Vehicles:takeVehicle", function(vehId, locations) position = "bottom" }) end + player.notify({ + title = "Impound", + description = ("Paid $%d to reclaim vehicle!"):format(reclaimPrice), + type = "success", + position = "bottom" + }) + MySQL.query.await("UPDATE nd_vehicles SET impounded = ? WHERE id = ?", {0, vehicle.id}) end local info = NDCore.spawnOwnedVehicle(src, vehicle.id, isParkingAvailable(locations)) From d5dfa02435815c9dbbe3fd13a95eb34abe252e2f Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:43:06 +0100 Subject: [PATCH 138/161] fix(server/player): location & dead metadata --- server/player.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/player.lua b/server/player.lua index 652f3b6..22aa986 100644 --- a/server/player.lua +++ b/server/player.lua @@ -152,7 +152,7 @@ local function createCharacterTable(info) self.setMetadata("location", { x = coords.x, y = coords.y, - x = coords.z, + z = coords.z, w = heading }) end @@ -264,8 +264,8 @@ local function createCharacterTable(info) function self.revive() self.triggerEvent("ND:revivePlayer") self.setMetadata({ - dead = nil, - deathInfo = nil, + dead = false, + deathInfo = false, }) end From cb96fdd3e677ef8536c02b3a3a15d27fc360dd12 Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 3 Nov 2023 16:28:54 +0100 Subject: [PATCH 139/161] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81cad49..0d2114b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
Discord -
Documentation
## Dependency:
* [oxmysql](https://github.com/overextended/oxmysql/releases)
From 623afab92ad0242a3f869992616ca22774674b0d Mon Sep 17 00:00:00 2001
From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com>
Date: Fri, 3 Nov 2023 16:38:02 +0100
Subject: [PATCH 140/161] fix(server/commands): return if player not found.
---
server/commands.lua | 3 +++
1 file changed, 3 insertions(+)
diff --git a/server/commands.lua b/server/commands.lua
index 8969728..af2bf41 100644
--- a/server/commands.lua
+++ b/server/commands.lua
@@ -84,6 +84,8 @@ lib.addCommand("setjob", {
}
}, function(source, args, raw)
local player = NDCore.getPlayer(args.target)
+ if not player then return end
+
local job = args.job:lower()
local jobInfo = player.setJob(job, args.rank)
if not player or not jobInfo then return end
@@ -210,6 +212,7 @@ lib.addCommand("pay", {
end
local player = NDCore.getPlayer(source)
+ if not player then return end
local success = player.deductMoney("cash", args.amount)
if not success then
From f22577d5dd7a5f86c2930a97e1129d2d499ac8e5 Mon Sep 17 00:00:00 2001
From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com>
Date: Sun, 5 Nov 2023 23:22:22 +0100
Subject: [PATCH 141/161] wip(compatiblity/esx): server side functions & more
---
compatibility/esx/server.lua | 343 +++++++++++++++++++++++++++++++++++
1 file changed, 343 insertions(+)
create mode 100644 compatibility/esx/server.lua
diff --git a/compatibility/esx/server.lua b/compatibility/esx/server.lua
new file mode 100644
index 0000000..e09f2d5
--- /dev/null
+++ b/compatibility/esx/server.lua
@@ -0,0 +1,343 @@
+local itemNames
+local registeredItems = {}
+
+local function getAmmoFromWeapon(weapon)
+ for item, data in pairs(exports.ox_inventory:Items()) do
+ if data.weapon and data.model:lower() == weapon:lower then
+ return data.ammoname
+ end
+ end
+end
+
+local function createPlayerFunctions(self)
+ function self.addAccountMoney(account, amount)
+ local amount = tonumber(amount)
+ if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
+ self[account] += amount
+ if NDCore.players[self.source] then
+ self.triggerEvent("ND:updateMoney", self.cash, self.bank)
+ TriggerEvent("ND:moneyChange", self.source, account, amount, "add")
+ end
+ return true
+ end
+
+ function self.addInventoryItem(item, count)
+ exports.ox_inventory:AddItem(self.source, item, count)
+ end
+
+ function self.addMoney(amount)
+ local amount = tonumber(amount)
+ if not amount or amount <= 0 then return end
+ self["bank"] += amount
+ if NDCore.players[self.source] then
+ self.triggerEvent("ND:updateMoney", self.cash, self.bank)
+ TriggerEvent("ND:moneyChange", self.source, "bank", amount, "add", reason)
+ end
+ return true
+ end
+
+ function self.addWeaponAmmo(weaponName, ammoCount)
+ local ammoName = getAmmoFromWeapon(weaponName)
+ if not ammoName then return end
+ self.addInventoryItem(ammoName, ammoCount)
+ end
+
+ function self.addWeapon(weaponName, ammo)
+ local name = nil
+ if weaponName:find("weapon") then
+ name = weaponName
+ else
+ name = ("weapon_%s"):format(weaponName)
+ end
+ self.addInventoryItem(name, 1)
+ self.addWeaponAmmo(name, ammo)
+
+ -- local weapon = GetHashKey(name)
+ -- local ped = GetPlayerPed(self.source)
+ -- GiveWeaponToPed(ped, weapon, ammo, false, false)
+ end
+
+ function self.addWeaponComponent(_, component)
+ self.addInventoryItem(component, 1)
+ end
+
+ function self.canCarryItem(item, count)
+ return exports.ox_inventory:CanCarryItem(self.source, item, count)
+ end
+
+ function self.canSwapItem(firstItem, firstItemCount, testItem, testItemCount)
+ exports.ox_inventory:CanSwapItem(self.source, firstItem, firstItemCount, testItem, testItemCount)
+ end
+
+ function self.clearMeta(index)
+ self.setMetadata(index, nil)
+ end
+
+ function self.getMeta(index, subIndex)
+ local meta = self.getMetadata(index)
+ if type(meta) == "table" then
+ return meta[subIndex]
+ end
+ return meta
+ end
+
+ function self.getCoords(useVector)
+ local ped = GetPlayerPed(self.source)
+ local coords = GetEntityCoords(ped)
+ if useVector then
+ return coords
+ end
+ return {
+ x = coords.x,
+ y = coords.y,
+ z = coords.z
+ }
+ end
+
+ function self.getIdentifier()
+ return self.identifier
+ end
+
+ function self.getInventory(minimal)
+ local playerItems = exports.ox_inventory:GetInventoryItems(self.source)
+ if not minimal then
+ return playerItems
+ end
+ -- minimal stuff
+ end
+
+ function self.getInventoryItem(item)
+ local playerItems = exports.ox_inventory:GetInventoryItems(self.source)
+ for name, data in pairs(playerItems) do
+ if name == item then
+ return data
+ end
+ end
+ end
+
+ function self.getMoney()
+ return self.bank
+ end
+
+ function self.getName()
+ return self.fullname
+ end
+
+ function self.hasItem(item, metadata)
+ return exports.ox_inventory:GetItem(self.source, item, metadata)
+ end
+
+ function self.removeInventoryItem(item, count)
+ exports.ox_inventory:RemoveItem(self.source, item, count)
+ end
+
+ function self.removeMoney(amount)
+ self.deductMoney("bank", amount)
+ end
+
+ function setMoney(amount)
+ self.setData("bank", amount)
+ end
+
+ self.kick = self.drop
+ self.removeAccountMoney = self.deductMoney
+ self.setMeta = self.setMetadata
+ self.setAccountMoney = self.setData
+ self.setInventoryItem = self.addInventoryItem
+
+
+ -- self.getAccount(account)
+ -- self.getAccounts()
+ -- self.getGroup()
+ -- self.getJob()
+ -- self.getLoadout()
+ -- self.getMissingAccounts(cb)
+ -- self.getWeapon(weaponName)
+ -- self.getWeaponTint(weaponName, weaponTintIndex)
+ -- self.getWeight()
+ -- self.hasWeapon(weaponName)
+ -- self.hasWeaponComponent(weaponName, weaponComponent)
+ -- self.removeWeapon(weaponName)
+ -- self.removeWeaponAmmo(weaponName, ammoCount)
+ -- self.removeWeaponComponent(weaponName, weaponComponent)
+ -- self.setMaxWeight(newWeight)
+ -- self.setName(newName)
+ -- self.setWeaponTint(weaponName, weaponTintIndex)
+ -- self.showAdvancedNotification(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
+ -- self.showHelpNotification(msg, thisFrame, beep, duration)
+ -- self.showNotification(msg, flash, saveToBrief, hudColorIndex)
+
+ return self
+end
+
+NDCore.OneSync = {}
+NDCore.RegisterServerCallback = lib.callback.register
+NDCore.SetTimeout = SetTimeout
+NDCore.Trace = Citizen.Trace
+
+local function exportHandler(resource, exportName, cb)
+ AddEventHandler(("__cfx_export_%s_%s"):format(resource, exportName), function(setCB)
+ setCB(cb)
+ end)
+end
+
+exportHandler("es_extended", "getSharedObject", function()
+ return NDCore
+end)
+
+function NDCore.ClearTimeout()
+ print("[^3WARNING^7] ESX Function 'ClearTimeout' is not compatible with NDCore!")
+end
+
+function NDCore.CreatePickup()
+ print("[^3WARNING^7] ESX Function 'CreatePickup' is not compatible with NDCore!")
+end
+
+function NDCore.DiscordLog()
+ print("[^3WARNING^7] ESX Function 'DiscordLog' is not compatible with NDCore!")
+end
+
+function NDCore.DiscordLogFields()
+ print("[^3WARNING^7] ESX Function 'DiscordLogFields' is not compatible with NDCore!")
+end
+
+function NDCore.RegisterUsableItem()
+ print("[^3WARNING^7] ESX Function 'RegisterUsableItem' is not compatible with NDCore!")
+end
+
+function NDCore.UseItem()
+ print("[^3WARNING^7] ESX Function 'UseItem' is not compatible with NDCore!")
+end
+
+
+function NDCore.GetPlayerFromId(src)
+ return createPlayerFunctions(NDCore.getPlayer(src))
+end
+
+function NDCore.GetExtendedPlayers(key, value)
+ local players = {}
+ if not key or not value then
+ for _, info in pairs(NDCore.players) do
+ players[#players+1] = createPlayerFunctions(info)
+ end
+ return players
+ end
+
+ local keyTypes = {id = "id", firstname = "firstname", lastname = "lastname", gender = "gender", groups = "groups"}
+ local findBy = keyTypes[key] or "metadata"
+ if findBy then
+ for _, info in pairs(NDCore.players) do
+ if findBy == "metadata" and info["metadata"][key] == value or info[findBy] == value then
+ players[#players+1] = createPlayerFunctions(info)
+ end
+ end
+ end
+ return players
+end
+
+NDCore.GetPlayers = NDCore.GetExtendedPlayers
+
+function NDCore.RegisterCommand(name, perms, cb, allowConsole, suggestion)
+ lib.addCommand(name, {
+ help = suggestion.help,
+ params = suggestion.arguments,
+ restricted = perms and ("group.%s"):format(perms)
+ }, function(source, args, raw)
+ if allowConsole and source == 0 then
+ return print("[^3WARNING^7] ^5Command Cannot be executed from console")
+ end
+ local player = NDCore.getPlayer(source)
+ cb(player, args, function(msg)
+ if source == 0 then
+ return print(("[^3WARNING^7] %s^7"):format(msg))
+ end
+ player.showNotification(msg)
+ end)
+ end)
+end
+
+function NDCore.DoesJobExist(job, grade)
+ local groupInfo = Config.groups[job]
+ if groupInfo and groupInfo.ranks[grade] then
+ return true
+ end
+end
+
+function NDCore.GetItemLabel(item)
+ if not itemNames then
+ itemNames = {}
+ for item, data in pairs(exports.ox_inventory:Items()) do
+ itemNames[item] = data.label
+ end
+ end
+ return itemNames[item]
+end
+
+function NDCore.GetJobs()
+ return Config.groups
+end
+
+function NDCore.GetPlayerFromIdentifier(identifier)
+ for _, info in pairs(NDCore.players) do
+ if info.identifier:find(identifier) then
+ return info
+ end
+ end
+end
+
+function NDCore.OneSync.SpawnObject(model, coords, heading, cb)
+ local entity = CreateObject(model, coords.x, coords.y, coords.z, true, false, false)
+ local value = lib.waitFor(function()
+ if DoesEntityExist(entity) then return entity end
+ end, "Failed to spawn object", 5000)
+ if not value then return end
+ SetEntityHeading(value, heading)
+ if not cb then return end
+ cb(value)
+end
+
+function NDCore.OneSync.SpawnPed(model, coords, heading, cb)
+ local entity = CreatePed(0, model, coords.x, coords.y, coords.z, heading, true, false)
+ local value = lib.waitFor(function()
+ if DoesEntityExist(entity) then return entity end
+ end, "Failed to spawn ped", 5000)
+ if not value or not cb then return end
+ cb(NetworkGetNetworkIdFromEntity(value))
+end
+
+function NDCore.OneSync.SpawnVehicle(model, coords, heading, properties, cb)
+ local vehicle = NDCore.createVehicle({
+ model = model,
+ coords = coords,
+ heading = heading
+ })
+ if not cb then return end
+ cb(vehicle.netId)
+end
+
+function NDCore.OneSync.SpawnPedInVehicle(model, vehicle, seat, cb)
+ local entity = CreatePedInsideVehicle(vehicle, 0, model, seat, true, false)
+ local value = lib.waitFor(function()
+ if DoesEntityExist(entity) then return entity end
+ end, "Failed to spawn ped", 5000)
+ if not value or not cb then return end
+ cb(value)
+end
+
+AddEventHandler("esx:playerDropped", function(src)
+ TriggerEvent("ND:characterUnloaded", src, NDCore.getPlayer(src))
+end
+
+AddEventHandler("esx:playerLoaded", function(src)
+ TriggerEvent("ND:characterLoaded", NDCore.getPlayer(src))
+end
+
+AddEventHandler("ND:characterUnloaded", function(src, character)
+ TriggerEvent("esx:playerDropped", src, "")
+end
+
+AddEventHandler("ND:characterLoaded", function(character)
+ TriggerEvent("esx:playerLoaded", character.source, createPlayerFunctions(character))
+end
+
+
From 954221e13ed188b05631f4da8f79d38db41a794f Mon Sep 17 00:00:00 2001
From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com>
Date: Sun, 5 Nov 2023 23:43:37 +0100
Subject: [PATCH 142/161] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 0d2114b..c2d99d2 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@
* [Character selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2)
* [Banking](https://github.com/ND-Framework/ND_Banking/tree/wip-v2)
* [Appearance shops](https://github.com/ND-Framework/ND_AppearanceShops/tree/wip-v2)
+* [Dealership](https://github.com/ND-Framework/ND_Dealership/tree/v2)
* [Inventory](https://github.com/overextended/ox_inventory/pull/1403)
# Need support?
From d0097203f5853da9a28b9381075f4582b5a2c21f Mon Sep 17 00:00:00 2001
From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com>
Date: Fri, 10 Nov 2023 23:12:13 +0100
Subject: [PATCH 143/161] Update README.md
---
README.md | 89 -------------------------------------------------------
1 file changed, 89 deletions(-)
diff --git a/README.md b/README.md
index c2d99d2..2ca3b52 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,6 @@
Discord -
Documentation
## Dependency:
@@ -19,90 +17,3 @@
# Need support?
[](https://discord.gg/Z9Mxu72zZ6)
-
-# Setup until docs is done:
-Paste code below into a file and name it ndcore.cfg then write `exec ndcore.cfg` in your server.cfg
-```py
-# Your servers name
-setr core:serverName "My FiveM Server"
-
-# Discord invite link
-setr core:discordInvite "https://discord.gg/Z9Mxu72zZ6"
-
-# Discord app id for rich presence
-setr core:discordAppId "858146067018416128"
-
-# Images for discord rich presence
-setr core:discordAsset "andyyy"
-setr core:discordAssetSmall "andyyy"
-
-# Buttons for discord rich presence
-setr core:discordActionText "DISCORD"
-setr core:discordActionLink "https://discord.gg/Z9Mxu72zZ6"
-
-setr core:discordActionText2 "STORE"
-setr core:discordActionLink2 "https://andyyy.tebex.io/category/fivem-scripts"
-
-# Used for getting users roles from your server, this can be useful for discord based scripts, if you don't add then it won't be used.
-# set core:discordGuildId "123456789012345678"
-# set core:discordBotToken "EXAMPLE_TOKEN.abc123.xyz456"
-
-# The identifier to use for characters. Players aren't allowed to join without it, license is good don't change unless you know what you're doing.
-set core:characterIdentifier "license"
-
-# % chance of random vehicles being unlocked.
-setr core:randomUnlockedVehicleChance 30
-
-# disable vehicle air contorl for cars and other land vehicles that's not supposed to do flips in air.
-setr core:disableVehicleAirControl true
-
-# If true it will use ox_inventory keys item for vehicles, if false it will use a keybind.
-setr core:useInventoryForKeys true
-
-# You can set admins here by their identifiers, admins will receive admin group in core and have access to group.admin ace perms.
-# You can also use Discord role ids, Admins get access to commands and more.
-set core:admins ["fivem:1459624", "fivem:1152629"]
-set core:adminDiscordRoles ["944284542758449212", "93422454258349612", "93345451558145232"]
-
-# Allow ox_lib to use commands, don't remove this!
-add_ace resource.ox_lib command.add_ace allow
-add_ace resource.ox_lib command.remove_ace allow
-add_ace resource.ox_lib command.add_principal allow
-add_ace resource.ox_lib command.remove_principal allow
-
-# This is jobs, gangs, police, fire, ambulance, everything.
-setr core:groups {
- "sahp": {
- "label": "SAHP",
- "ranks": ["Trooper", "Senior Trooper", "Corporal", "Sergeant", "Lieutenant", "Chief"]
- },
- "lspd": {
- "label": "LSPD",
- "ranks": ["Officer", "Senior officer", "Corporal", "Sergeant", "Lieutenant", "Chief"]
- },
- "bcso": {
- "label": "BCSO",
- "ranks": ["Officer", "Senior officer", "Corporal", "Sergeant", "Lieutenant", "Chief"]
- },
- "swat": {
- "label": "SWAT",
- "ranks": ["Member", "Sniper", "Team lead", "Commander"]
- },
- "lsfd": {
- "label": "LSFD",
- "ranks": ["Volunteer", "Firefighter", "Senior firefighter", "Lieutenant", "Fire Chief"]
- },
- "ballas": {
- "label": "Ballas",
- "ranks": ["Member", "Leader"]
- },
- "families": {
- "label": "Families",
- "ranks": ["Member", "Leader"]
- },
- "cartel": {
- "label": "Madrazo Cartel",
- "ranks": ["Member", "Leader"]
- }
-}
-```
From b319d4a90a4e77a6933efd4e7a93efd7aa362269 Mon Sep 17 00:00:00 2001
From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com>
Date: Fri, 10 Nov 2023 23:26:05 +0100
Subject: [PATCH 144/161] Update README.md
---
README.md | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 2ca3b52..f30b990 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,15 @@
Documentation -## Dependency: +
+
+
-
+
+
+
Documentation From bad05a81ca7491006fb00d431979bb92ea77cec0 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 11 Nov 2023 19:01:40 +0100 Subject: [PATCH 148/161] feat: esx backwards compatibility --- compatibility/esx/client.lua | 62 +++++++++++++++++++++++++----------- compatibility/esx/locale.lua | 29 +++++++++++++++++ compatibility/esx/server.lua | 52 ++++++++++++++++++++++-------- fxmanifest.lua | 4 ++- 4 files changed, 115 insertions(+), 32 deletions(-) create mode 100644 compatibility/esx/locale.lua diff --git a/compatibility/esx/client.lua b/compatibility/esx/client.lua index 62b667e..68d65f2 100644 --- a/compatibility/esx/client.lua +++ b/compatibility/esx/client.lua @@ -8,6 +8,7 @@ NDCore.Streaming = {} NDCore.UI = {} NDCore.UI.HUD = {} NDCore.UI.Menu = {} +NDCore.PlayerData = {} local uiMetatable = { __index = function(table, key) @@ -35,6 +36,7 @@ end) function NDCore.GetPlayerData() local player = NDCore.getPlayer() + if not player then return {} end player.Accounts = { Bank = player.bank, @@ -42,7 +44,31 @@ function NDCore.GetPlayerData() Black = player.Cash } - -- add more here + if player.jobInfo then + player.job = { + id = player.job, + name = player.job, + label = player.jobInfo.label, + grade = player.jobInfo.rank, + grade_name = player.jobInfo.rankName, + grade_label = player.jobInfo.rankName, + grade_salary = 0, + skin_male = {}, + skin_female = {} + } + end + + player.coords = GetEntityCoords(cache.ped) + player.loadout = {} + player.maxWeight = exports.ox_inventory:GetPlayerMaxWeight() + player.money = player.Accounts.Money + player.sex = player.gender + player.firstName = player.firstname + player.lastName = player.lastname + player.dateofbirth = player.dob + player.height = 120 + player.dead = LocalPlayer.state.dead or false + NDCore.PlayerData = player return player end @@ -62,7 +88,7 @@ function NDCore.Progressbar(message, lenght, options) newOptions.anim = {} if options.animation.type == "anim" then if options.animation.dict then - newOptions.anim.dict = options.animation.dict, + newOptions.anim.dict = options.animation.dict end if options.animation.lib then newOptions.anim.clip = options.animation.lib @@ -319,11 +345,9 @@ function NDCore.Game.SpawnLocalObject(model, coords, cb) end local entity = CreateObject(model, coords.x, coords.y, coords.z, false, false, false) entity = lib.waitFor(function() - if DoesEntityExist(entity) end + if DoesEntityExist(entity) then return entity end end) - if cb then - cb(entity) - end + if cb then cb(entity) end return entity end @@ -333,11 +357,9 @@ function NDCore.Game.SpawnLocalVehicle(model, coords, heading, cb) end local entity = CreateVehicle(model, coords.x, coords.y, coords.z, heading, false, false, false) entity = lib.waitFor(function() - if DoesEntityExist(entity) end + if DoesEntityExist(entity) then return entity end end) - if cb then - cb(entity) - end + if cb then cb(entity) end return entity end @@ -347,11 +369,9 @@ function NDCore.Game.SpawnObject(model, coords, cb) end local entity = CreateObject(model, coords.x, coords.y, coords.z, true, false, false) entity = lib.waitFor(function() - if DoesEntityExist(entity) end + if DoesEntityExist(entity) then return entity end end) - if cb then - cb(entity) - end + if cb then cb(entity) end return entity end @@ -361,11 +381,9 @@ function NDCore.Game.SpawnVehicle(model, coords, heading, cb) end local entity = CreateVehicle(model, coords.x, coords.y, coords.z, heading, true, false, false) entity = lib.waitFor(function() - if DoesEntityExist(entity) end + if DoesEntityExist(entity) then return entity end end) - if cb then - cb(entity) - end + if cb then cb(entity) end return entity end @@ -384,3 +402,11 @@ function NDCore.Game.Teleport(entity, coords, cb) cb() end end + +AddEventHandler("ND:characterLoaded", function() + NDCore.GetPlayerData() +end) + +AddEventHandler("ND:updateCharacter", function() + NDCore.GetPlayerData() +end) diff --git a/compatibility/esx/locale.lua b/compatibility/esx/locale.lua new file mode 100644 index 0000000..9ec37e5 --- /dev/null +++ b/compatibility/esx/locale.lua @@ -0,0 +1,29 @@ +local config_locale = "en" +Locales = {} + +function Translate(str, ...) -- Translate string + if not str then + print(("[^1ERROR^7] Resource ^5%s^7 You did not specify a parameter for the Translate function or the value is nil!"):format(GetInvokingResource() or GetCurrentResourceName())) + return 'Given translate function parameter is nil!' + end + if Locales[config_locale] then + if Locales[config_locale][str] then + return string.format(Locales[config_locale][str], ...) + elseif config_locale ~= 'en' and Locales['en'] and Locales['en'][str] then + return string.format(Locales['en'][str], ...) + else + return 'Translation [' .. config_locale .. '][' .. str .. '] does not exist' + end + elseif config_locale ~= 'en' and Locales['en'] and Locales['en'][str] then + return string.format(Locales['en'][str], ...) + else + return 'Locale [' .. config_locale .. '] does not exist' + end +end + +function TranslateCap(str, ...) -- Translate string first char uppercase + return _(str, ...):gsub("^%l", string.upper) +end + +_ = Translate +_U = TranslateCap diff --git a/compatibility/esx/server.lua b/compatibility/esx/server.lua index 41a1b3e..3c195d2 100644 --- a/compatibility/esx/server.lua +++ b/compatibility/esx/server.lua @@ -4,14 +4,50 @@ local itemNames local registeredItems = {} local function getAmmoFromWeapon(weapon) + if not weapon then return end for item, data in pairs(exports.ox_inventory:Items()) do - if data.weapon and data.model:lower() == weapon:lower then + if data.weapon and data.model and data.model:lower() == weapon:lower() then return data.ammoname end end end local function createPlayerFunctions(self) + self.Accounts = { + Bank = self.bank, + Money = self.Cash, + Black = self.Cash + } + + if self.jobInfo then + self.job = { + id = self.job, + name = self.job, + label = self.jobInfo.label, + grade = self.jobInfo.rank, + grade_name = self.jobInfo.rankName, + grade_label = self.jobInfo.rankName, + grade_salary = 0, + skin_male = {}, + skin_female = {} + } + end + + local ped = GetPlayerPed(self.source) + if DoesEntityExist(ped) then + self.coords = GetEntityCoords() + end + + self.loadout = {} + self.maxWeight = 30000 + self.money = self.Accounts.Money + self.sex = self.gender + self.firstName = self.firstname + self.lastName = self.lastname + self.dateofbirth = self.dob + self.height = 120 + self.dead = self.getMetadata("dead") + function self.addAccountMoney(account, amount) local amount = tonumber(amount) if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end @@ -326,20 +362,10 @@ function NDCore.OneSync.SpawnPedInVehicle(model, vehicle, seat, cb) cb(value) end -AddEventHandler("esx:playerDropped", function(src) - TriggerEvent("ND:characterUnloaded", src, NDCore.getPlayer(src)) -end - -AddEventHandler("esx:playerLoaded", function(src) - TriggerEvent("ND:characterLoaded", NDCore.getPlayer(src)) -end - AddEventHandler("ND:characterUnloaded", function(src, character) TriggerEvent("esx:playerDropped", src, "") -end +end) AddEventHandler("ND:characterLoaded", function(character) TriggerEvent("esx:playerLoaded", character.source, createPlayerFunctions(character)) -end - - +end) diff --git a/fxmanifest.lua b/fxmanifest.lua index 35cd0a8..0ea2ae7 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -33,7 +33,9 @@ server_scripts { files { "init.lua", - "client/vehicle/data.lua" + "client/vehicle/data.lua", + "compatibility/**/locale.lua" } dependency "oxmysql" +provide "es_extended" From 666c3125a3f0994fbdc00e85b5c27bd772c3049e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 11 Nov 2023 19:10:05 +0100 Subject: [PATCH 149/161] wip: qb backwards compatiblity --- compatibility/esx/.gitkeep | 0 compatibility/qb/.gitkeep | 0 compatibility/qb/client.lua | 2 ++ compatibility/qb/server.lua | 1 + fxmanifest.lua | 1 + 5 files changed, 4 insertions(+) delete mode 100644 compatibility/esx/.gitkeep delete mode 100644 compatibility/qb/.gitkeep create mode 100644 compatibility/qb/client.lua create mode 100644 compatibility/qb/server.lua diff --git a/compatibility/esx/.gitkeep b/compatibility/esx/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/compatibility/qb/.gitkeep b/compatibility/qb/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/compatibility/qb/client.lua b/compatibility/qb/client.lua new file mode 100644 index 0000000..93be1f2 --- /dev/null +++ b/compatibility/qb/client.lua @@ -0,0 +1,2 @@ +if not lib.table.contains(Config.compatibility, "qb") then return end + diff --git a/compatibility/qb/server.lua b/compatibility/qb/server.lua new file mode 100644 index 0000000..54e6f48 --- /dev/null +++ b/compatibility/qb/server.lua @@ -0,0 +1 @@ +if not lib.table.contains(Config.compatibility, "qb") then return end diff --git a/fxmanifest.lua b/fxmanifest.lua index 0ea2ae7..61de284 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -39,3 +39,4 @@ files { dependency "oxmysql" provide "es_extended" +provide "qb-Core" From 53255bf3365df2473e0be1658bf1d3831e13195a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 19 Nov 2023 00:07:35 +0100 Subject: [PATCH 150/161] tweak: checking vehicle when setting properties --- client/vehicle/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/vehicle/main.lua b/client/vehicle/main.lua index 82814a1..9c3bd56 100644 --- a/client/vehicle/main.lua +++ b/client/vehicle/main.lua @@ -285,8 +285,8 @@ end) AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, replicated) local entity = GetEntityFromStateBagName(bagName) - if entity == 0 or not value or not NetworkGetEntityOwner(entity) == cache.playerId then return end - lib.setVehicleProperties(entity, value) + if not value or not DoesEntityExist(entity) or NetworkGetEntityOwner(entity) ~= cache.playerId then return end + lib.setVehicleProperties(entity, json.decode(value)) end) local function playKeyFob(veh) From 7cc3bbe42aec751120aeb474d440be8bbe18aaf1 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 19 Nov 2023 00:09:45 +0100 Subject: [PATCH 151/161] refactor: revive function --- client/functions.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/client/functions.lua b/client/functions.lua index 074e0a5..be8d39f 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -28,6 +28,14 @@ end function NDCore.revivePlayer(reset, keepDead) local usingAmbulance = GetResourceState("ND_Ambulance") == "started" + if not keepDead then + LocalPlayer.state.dead = false + if usingAmbulance then + local state = Player(cache.serverId).state + state:set("isDead", false, true) + end + end + local veh = GetVehiclePedIsIn(cache.ped) local seat = cache.seat local coords = GetEntityCoords(cache.ped) @@ -36,7 +44,7 @@ function NDCore.revivePlayer(reset, keepDead) local ped = PlayerPedId() if cache.ped ~= ped then DeleteEntity(cache.ped) - ClearAreaOfPeds(coords.x, coords.y, coords.z, 1.5, false) + ClearAreaOfPeds(coords.x, coords.y, coords.z, 0.2, false) end SetEntityInvincible(ped, false) @@ -52,9 +60,6 @@ function NDCore.revivePlayer(reset, keepDead) if veh and veh ~= 0 then SetPedIntoVehicle(ped, veh, seat) end - if not keepDead then - LocalPlayer.state.dead = false - end if reset and GetPedMovementClipset(ped) == `move_m@injured` then ClearEntityLastDamageEntity(ped) SetPedMoveRateOverride(ped, 1.0) From 700d422477005a1ab824f1d122a215339e60ea8a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 19 Nov 2023 01:41:20 +0100 Subject: [PATCH 152/161] fix: setting vehicle props --- client/vehicle/main.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/vehicle/main.lua b/client/vehicle/main.lua index 9c3bd56..7a44c22 100644 --- a/client/vehicle/main.lua +++ b/client/vehicle/main.lua @@ -286,7 +286,11 @@ end) AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, replicated) local entity = GetEntityFromStateBagName(bagName) if not value or not DoesEntityExist(entity) or NetworkGetEntityOwner(entity) ~= cache.playerId then return end - lib.setVehicleProperties(entity, json.decode(value)) + local props = value + if type(value) == "string" then + props = json.decode(value) + end + lib.setVehicleProperties(entity, props) end) local function playKeyFob(veh) From ce29f21a907673af7c51a32a9b0eb7f3b1d6cd8e Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:54:46 +0100 Subject: [PATCH 153/161] tweak: get invoking resource on create ped --- client/peds.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/peds.lua b/client/peds.lua index 7a4a16f..2601c72 100644 --- a/client/peds.lua +++ b/client/peds.lua @@ -120,7 +120,7 @@ function NDCore.createAiPed(info) point = point, blip = blip, options = info.options, - resource = GetInvokingResource() + resource = info.resource or GetInvokingResource() } function point:onEnter() From 2b7b6c93b2b9bc94d55b4cb7e498e991f182993b Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:07:19 +0100 Subject: [PATCH 154/161] Update fxmanifest.lua --- fxmanifest.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fxmanifest.lua b/fxmanifest.lua index 61de284..4fb30f1 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -38,5 +38,5 @@ files { } dependency "oxmysql" -provide "es_extended" -provide "qb-Core" +-- provide "es_extended" +-- provide "qb-Core" From d46d0733fa46bb0d588400820a21edc9824bca71 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 2 Dec 2023 16:07:40 +0100 Subject: [PATCH 155/161] feat(client/peds): option to hide blip on range --- client/peds.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/peds.lua b/client/peds.lua index 2601c72..4ecc0fb 100644 --- a/client/peds.lua +++ b/client/peds.lua @@ -146,9 +146,16 @@ function NDCore.createAiPed(info) Wait(500) ox_target:addLocalEntity({ped}, options) end + + if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) then + SetBlipAlpha(blip, 255) + end end function point:onExit() + if blip and blipInfo and blipInfo.showWhenNear and DoesBlipExist(blip) then + SetBlipAlpha(blip, 0) + end if ped and DoesEntityExist(ped) then if target and options then ox_target:removeLocalEntity({ped}) From 6764ac51a8f007700bbc28a6800584e7839fad54 Mon Sep 17 00:00:00 2001 From: Hakko <67168651+Hakkodevelopment@users.noreply.github.com> Date: Sun, 3 Dec 2023 08:20:56 -0700 Subject: [PATCH 156/161] fix: Discord Convars causing config to break --- server/main.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/main.lua b/server/main.lua index b09ee76..359201f 100644 --- a/server/main.lua +++ b/server/main.lua @@ -15,8 +15,8 @@ Config = { discordActionText2 = GetConvar("core:discordActionText2", "STORE"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), characterIdentifier = GetConvar("core:characterIdentifier", "license"), - discordGuildId = GetConvar("core:discordGuildId"), - discordBotToken = GetConvar("core:discordBotToken"), + discordGuildId = GetConvar("core:discordGuildId", 'false'), + discordBotToken = GetConvar("core:discordBotToken", 'false'), randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, @@ -68,7 +68,7 @@ AddEventHandler("playerJoining", function(oldId) end) local function checkDiscordIdentifier(identifiers) - if not Config.discordBotToken or not Config.discordGuildId then return end + if Config.discordBotToken == 'false'or Config.discordGuildId == 'false' then return end local discordIdentifier = identifiers["discord"] if not discordIdentifier then return end @@ -77,7 +77,7 @@ local function checkDiscordIdentifier(identifiers) end AddEventHandler("onResourceStart", function(name) - if name ~= resourceName or not Config.discordBotToken or not Config.discordGuildId then return end + if name ~= resourceName or Config.discordBotToken == 'false' or Config.discordGuildId == 'false' then return end for _, playerId in ipairs(GetPlayers()) do local src = tonumber(playerId) local identifiers = getIdentifierList(src) @@ -98,7 +98,7 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) deferrals.defer() Wait(0) - if mainIdentifier then + if mainIdentifier and Config.discordBotToken == 'false' and Config.discordGuildId == 'false' and not discordInfo then discordInfo = checkDiscordIdentifier(identifiers) if not discordInfo then deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite)) From e064af5a8b5e530040c4a3d2e768c99f656a8dd2 Mon Sep 17 00:00:00 2001 From: Hakko <67168651+Hakkodevelopment@users.noreply.github.com> Date: Sun, 3 Dec 2023 08:38:35 -0700 Subject: [PATCH 157/161] fixed errors, sorry im tired --- server/main.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/main.lua b/server/main.lua index 359201f..ed7db03 100644 --- a/server/main.lua +++ b/server/main.lua @@ -15,8 +15,8 @@ Config = { discordActionText2 = GetConvar("core:discordActionText2", "STORE"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), characterIdentifier = GetConvar("core:characterIdentifier", "license"), - discordGuildId = GetConvar("core:discordGuildId", 'false'), - discordBotToken = GetConvar("core:discordBotToken", 'false'), + discordGuildId = GetConvar("core:discordGuildId", "false"), + discordBotToken = GetConvar("core:discordBotToken", "false"), randomUnlockedVehicleChance = GetConvarInt("core:randomUnlockedVehicleChance", 30), disableVehicleAirControl = GetConvarInt("core:disableVehicleAirControl", 1) == 1, useInventoryForKeys = GetConvarInt("core:useInventoryForKeys", 1) == 1, @@ -68,7 +68,7 @@ AddEventHandler("playerJoining", function(oldId) end) local function checkDiscordIdentifier(identifiers) - if Config.discordBotToken == 'false'or Config.discordGuildId == 'false' then return end + if Config.discordBotToken == "false"or Config.discordGuildId == "false" then return end local discordIdentifier = identifiers["discord"] if not discordIdentifier then return end @@ -77,7 +77,7 @@ local function checkDiscordIdentifier(identifiers) end AddEventHandler("onResourceStart", function(name) - if name ~= resourceName or Config.discordBotToken == 'false' or Config.discordGuildId == 'false' then return end + if name ~= resourceName or Config.discordBotToken == "false" or Config.discordGuildId == "false" then return end for _, playerId in ipairs(GetPlayers()) do local src = tonumber(playerId) local identifiers = getIdentifierList(src) @@ -98,7 +98,7 @@ AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) deferrals.defer() Wait(0) - if mainIdentifier and Config.discordBotToken == 'false' and Config.discordGuildId == 'false' and not discordInfo then + if mainIdentifier and Config.discordBotToken ~= "false" and Config.discordGuildId ~= "false" and not discordInfo then discordInfo = checkDiscordIdentifier(identifiers) if not discordInfo then deferrals.done(("Your discord was not found, join our discord here: %s."):format(Config.discordInvite)) From 0df4077bfe7395aa8608b464ce57fd453d7d1cdf Mon Sep 17 00:00:00 2001 From: Hakko <67168651+Hakkodevelopment@users.noreply.github.com> Date: Sun, 3 Dec 2023 08:41:46 -0700 Subject: [PATCH 158/161] again.. im tired --- server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/main.lua b/server/main.lua index ed7db03..67932ed 100644 --- a/server/main.lua +++ b/server/main.lua @@ -68,7 +68,7 @@ AddEventHandler("playerJoining", function(oldId) end) local function checkDiscordIdentifier(identifiers) - if Config.discordBotToken == "false"or Config.discordGuildId == "false" then return end + if Config.discordBotToken == "false" or Config.discordGuildId == "false" then return end local discordIdentifier = identifiers["discord"] if not discordIdentifier then return end From 6f4f94df96363c211d3ab8f0dbe18752c90d249d Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:43:51 +0100 Subject: [PATCH 159/161] fix: getting identifiers on resource start --- server/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/main.lua b/server/main.lua index 67932ed..fd9fa68 100644 --- a/server/main.lua +++ b/server/main.lua @@ -77,13 +77,13 @@ local function checkDiscordIdentifier(identifiers) end AddEventHandler("onResourceStart", function(name) - if name ~= resourceName or Config.discordBotToken == "false" or Config.discordGuildId == "false" then return end + if name ~= resourceName then return end for _, playerId in ipairs(GetPlayers()) do local src = tonumber(playerId) local identifiers = getIdentifierList(src) PlayersInfo[src] = { identifiers = identifiers, - discord = checkDiscordIdentifier(identifiers) + discord = checkDiscordIdentifier(identifiers) or {} } Wait(65) end From d76293d117acd621d8be3e15b8415edc3dbc4b5b Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 4 Dec 2023 21:27:17 +0100 Subject: [PATCH 160/161] fix: remove selecting money from backwards comptibility --- compatibility/backwards/server.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compatibility/backwards/server.lua b/compatibility/backwards/server.lua index 172a692..fb72827 100644 --- a/compatibility/backwards/server.lua +++ b/compatibility/backwards/server.lua @@ -219,8 +219,8 @@ RegisterNetEvent("ND:newCharacter", function(newCharacter) lastname = newCharacter.lastName, dob = newCharacter.dob, gender = newCharacter.gender, - cash = newCharacter.cash, - bank = newCharacter.bank, + cash = 0, + bank = 0, }) end) From f65728c90aa8b29e48f45a467052577341edad5a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Sat, 9 Dec 2023 19:44:12 +0100 Subject: [PATCH 161/161] tweak(server/player): inventory saving --- server/player.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/player.lua b/server/player.lua index 22aa986..410ffd9 100644 --- a/server/player.lua +++ b/server/player.lua @@ -165,7 +165,7 @@ local function createCharacterTable(info) -- Save character information to database function self.save() - local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ?, inventory = ? WHERE charid = ?", { + local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ? WHERE charid = ?", { self.name, self.firstname, self.lastname, @@ -175,7 +175,6 @@ local function createCharacterTable(info) self.bank, json.encode(self.groups), json.encode(self.metadata), - json.encode(self.inventory), self.id }) return affectedRows > 0