From 9a9fa03e8a994ed6ecd7dfc320d950e4d00589a1 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:37:54 +0200 Subject: [PATCH 1/7] Revert "a lot of changes" This reverts commit bf9604e851b58510d8755111264700b8999227d5. --- client/events.lua | 2 +- client/functions.lua | 6 -- client/main.lua | 2 - fxmanifest.lua | 9 +- init.lua | 15 +-- server/functions.lua | 20 ---- server/main.lua | 16 ++- server/player.lua | 248 +++++++++++++++++++++---------------------- 8 files changed, 146 insertions(+), 172 deletions(-) delete mode 100644 server/functions.lua diff --git a/client/events.lua b/client/events.lua index 8ae1397..791dc64 100644 --- a/client/events.lua +++ b/client/events.lua @@ -29,4 +29,4 @@ AddEventHandler("playerSpawned", function() print("^0ND Framework support discord: ^5https://discord.gg/Z9Mxu72zZ6") SetCanAttackFriendly(PlayerPedId(), true, false) NetworkSetFriendlyFireOption(true) -end) +end) \ No newline at end of file diff --git a/client/functions.lua b/client/functions.lua index 4b1e75f..3fa06a3 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -17,9 +17,3 @@ function NDCore.GetPlayersFromCoords(distance, coords) end return closePlayers end - -for name, func in pairs(NDCore) do - if type(func) == "function" then - exports(name, func) - end -end diff --git a/client/main.lua b/client/main.lua index 7038aa9..1cef364 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,5 +1,3 @@ -NDCore = {} - CreateThread(function() SetDiscordAppId(Config.discordAppId) SetDiscordRichPresenceAsset(Config.discordAsset) diff --git a/fxmanifest.lua b/fxmanifest.lua index 9ec131d..e084ad9 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -8,19 +8,18 @@ fx_version "cerulean" game "gta5" lua54 "yes" +shared_scripts { + "init.lua" +} client_scripts { - "client/main.lua", "client/functions.lua", "client/events.lua", + "client/main.lua" } server_scripts { "@oxmysql/lib/MySQL.lua", "server/main.lua", - "server/functions.lua", "server/player.lua" } -shared_scripts { - "init.lua" -} dependency "oxmysql" diff --git a/init.lua b/init.lua index b0540a3..b4ded23 100644 --- a/init.lua +++ b/init.lua @@ -8,16 +8,9 @@ Config = { discordActionLink = GetConvar("discordActionLink", "https://discord.gg/Z9Mxu72zZ6"), discordActionText2 = GetConvar("core:discordActionText2", "STORE"), discordActionLink2 = GetConvar("core:discordActionLink2", "https://andyyy.tebex.io/category/fivem-scripts"), + } -local nd_core = exports["ND_Core"] - -NDCore = setmetatable({}, { - __index = function(self, index) - self[index] = function(...) - return nd_core[index](nil, ...) - end - - return self[index] - end -}) +NDCore = { + config = Config +} diff --git a/server/functions.lua b/server/functions.lua deleted file mode 100644 index 8d09f33..0000000 --- a/server/functions.lua +++ /dev/null @@ -1,20 +0,0 @@ -function NDCore.getPlayer(src) - return ActivePlayers[src] -end - -function NDCore.getPlayers(metadata, data) - if not metadata or not data then return ActivePlayers end - local players = {} - for src, info in pairs(ActivePlayers) do - if info.metadata[metadata] == data then - players[src] = info - end - end - return players -end - -for name, func in pairs(NDCore) do - if type(func) == "function" then - exports(name, func) - end -end diff --git a/server/main.lua b/server/main.lua index a06c33b..0b66541 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,6 +1,20 @@ -NDCore = {} ActivePlayers = {} +function NDCore.getPlayer(src) + return ActivePlayers[src] +end + +function NDCore.getPlayers(metadata, data) + if not metadata or not data then return ActivePlayers end + local players = {} + for src, info in pairs(ActivePlayers) do + if info.metadata[metadata] == data then + players[src] = info + end + end + return players +end + AddEventHandler("playerDropped", function() local src = source local char = ActivePlayers[src] diff --git a/server/player.lua b/server/player.lua index 6efda90..5f79381 100644 --- a/server/player.lua +++ b/server/player.lua @@ -1,137 +1,133 @@ -local function createCharacterTable(info) - local self = { - source = info.source, - name = info.name, - firstname = info.firstname, - lastname = info.lastname, - dob = info.dob, - gender = info.gender, - cash = info.cash, - bank = info.bank, - license = info.license, - 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 - - 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 - - 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 - - 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") - end - - 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") - end - - 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 - - function self.unload(self) - TriggerEvent("ND:characterUnloaded", self.source, self) - local ped = GetPlayerPed(self.source) - self:setMetadata("location", GetEntityCoords(ped)) - self:save() - if not self.source then return end +local function charDelete(self) + local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id}) + if result and self.source then 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 = ?", { - self.name, - self.firstname, - self.lastname, - self.dob, - self.gender, - self.cash, - self.bank, - json.encode(self.metadata), - json.encode(self.inventory), - self.id, - }) - end - - function self.createLicense(licenseType, expire) - local expireIn = tonumber(expire) or 2592000 - local time = os.time() - local licenses = self.metadata.licenses - local identifier = {} - - for i=1, 16 do - identifier[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) - end - - local license = { - type = licenseType, - status = "valid", - issued = time, - expires = time+expireIn, - identifier = table.concat(identifier) - } - - if licenses then - self.metadata.licenses[#licenses+1] = license - return - end - self.metadata.licenses = {license} - - if not self.source then return end + return result +end + +local function charDeductMoney(self, 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 - - 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) - return true + return true +end + +local function charAddMoney(self, 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 + +local function charDepositMoney(self, 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") +end + +local function charWithdrawMoney(self, 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") +end + +local function charSetMetadata(self, key, value) + self.metadata[key] = value + if self.source then + ActivePlayers[self.source] = self + return ActivePlayers[self.source].metadata + end + return self.metadata +end + +local function charUnload(self) + TriggerEvent("ND:characterUnloaded", self.source, self) + local ped = GetPlayerPed(self.source) + self:setMetadata("location", GetEntityCoords(ped)) + self:save() + if not self.source then return end + ActivePlayers[self.source] = nil +end + +local function charSave(self) + MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, metadata = ?, inventory = ? WHERE charid = ?", { + self.name, + self.firstname, + self.lastname, + self.dob, + self.gender, + self.cash, + self.bank, + json.encode(self.metadata), + json.encode(self.inventory), + self.id, + }) +end + +local function charCreateLicense(self, licenseType, expire) + local expireIn = tonumber(expire) or 2592000 + local time = os.time() + local licenses = self.metadata.licenses + local identifier = {} + + for i=1, 16 do + identifier[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) end - return self + local license = { + type = licenseType, + status = "valid", + issued = time, + expires = time+expireIn, + identifier = table.concat(identifier) + } + + if licenses then + self.metadata.licenses[#licenses+1] = license + return + end + self.metadata.licenses = {license} + + if not self.source then return end + ActivePlayers[self.source] = self +end + +local function charSetCoords(self, 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) + return true +end + +local function initPlayerTable(playerTable) + playerTable.delete = charDelete, + playerTable.deductMoney = charDeductMoney, + playerTable.addMoney = charAddMoney + playerTable.depositMoney = charDepositMoney, + playerTable.withdrawMoney = charWithdrawMoney, + playerTable.setMetadata = charSetMetadata, + playerTable.save = charSave, + playerTable.unload = charUnload, + playerTable.createLicense = charCreateLicense + playerTable.setCoords = charSetCoords + return playerTable end function NDCore.newCharacter(src, info) local license = GetPlayerIdentifierByType(src, "license") if not license then return end - local charInfo = createCharacterTable({ + local charInfo = initPlayerTable({ source = src, name = GetPlayerName(src) or "", firstname = info.firstname or "", @@ -155,7 +151,7 @@ function NDCore.newCharacter(src, info) charInfo.cash, charInfo.bank, charInfo.metadata - }) + ) return charInfo end @@ -165,7 +161,7 @@ function NDCore.fetchCharacter(id) if not result then return end local info = result[1] - return createCharacterTable({ + return initPlayerTable({ id = id, name = info.name, firstname = info.firstname, @@ -187,7 +183,7 @@ function NDCore.getPlayerCharacters(src) for i=1, amount do local info = result[i] - characters[info.charid] = createCharacterTable({ + characters[info.charid] = initPlayerTable({ id = info.charid, name = info.name, firstname = info.firstname, @@ -209,7 +205,7 @@ function NDCore.setActiveCharacter(src, id) if char then char:unload() end local character = NDCore.fetchCharacter(id) - character.source = src + character.source = src, character.name = GetPlayerName(src) ActivePlayers[src] = character From 975b031a02feed3ec53489eaad1fae30620a615d Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:38:02 +0200 Subject: [PATCH 2/7] Revert "feat: get all players & by metadata" This reverts commit 24caa1a8573359830e0d65fb7d284918f290c6f4. --- server/main.lua | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/server/main.lua b/server/main.lua index 0b66541..d13951d 100644 --- a/server/main.lua +++ b/server/main.lua @@ -4,17 +4,6 @@ function NDCore.getPlayer(src) return ActivePlayers[src] end -function NDCore.getPlayers(metadata, data) - if not metadata or not data then return ActivePlayers end - local players = {} - for src, info in pairs(ActivePlayers) do - if info.metadata[metadata] == data then - players[src] = info - end - end - return players -end - AddEventHandler("playerDropped", function() local src = source local char = ActivePlayers[src] From 593443cae5915f5f5ad242ab3151d6899fdbb15a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:39:52 +0200 Subject: [PATCH 3/7] Revert "feat(server/player): set coords function" This reverts commit abad068eb747274071c40ff355bd1e80792d8f8e. --- server/player.lua | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/player.lua b/server/player.lua index 5f79381..952d877 100644 --- a/server/player.lua +++ b/server/player.lua @@ -100,13 +100,6 @@ local function charCreateLicense(self, licenseType, expire) if not self.source then return end ActivePlayers[self.source] = self end - -local function charSetCoords(self, 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) - return true end local function initPlayerTable(playerTable) @@ -119,7 +112,6 @@ local function initPlayerTable(playerTable) playerTable.save = charSave, playerTable.unload = charUnload, playerTable.createLicense = charCreateLicense - playerTable.setCoords = charSetCoords return playerTable end From 5389ae09624f31baf5f45136b8c842091e6b638a Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:40:39 +0200 Subject: [PATCH 4/7] revert, 2.0.0 is in new branch --- client/events.lua | 18 +- client/functions.lua | 65 +++- client/main.lua | 84 +++-- config_client.lua | 74 +++++ config_server.lua | 6 + fxmanifest.lua | 24 +- init.lua | 16 - server/commands.lua | 117 +++++++ server/events.lua | 117 +++++++ server/functions.lua | 766 +++++++++++++++++++++++++++++++++++++++++++ server/main.lua | 25 +- shared/import.lua | 1 + shared/main.lua | 30 ++ 13 files changed, 1265 insertions(+), 78 deletions(-) create mode 100644 config_client.lua create mode 100644 config_server.lua delete mode 100644 init.lua create mode 100644 server/commands.lua create mode 100644 server/events.lua create mode 100644 server/functions.lua create mode 100644 shared/import.lua create mode 100644 shared/main.lua diff --git a/client/events.lua b/client/events.lua index 791dc64..d2a034f 100644 --- a/client/events.lua +++ b/client/events.lua @@ -4,29 +4,21 @@ end) -- updates the money on the client. RegisterNetEvent("ND:updateMoney", function(cash, bank) - NDCore.player.cash = cash - NDCore.player.bank = bank + NDCore.SelectedCharacter.cash = cash + NDCore.SelectedCharacter.bank = bank end) -- Sets main character. RegisterNetEvent("ND:setCharacter", function(character) - NDCore.player = character + NDCore.SelectedCharacter = character end) -- Update main character info. RegisterNetEvent("ND:updateCharacter", function(character) - NDCore.player = character + NDCore.SelectedCharacter = character end) -- Updates last lcoation. RegisterNetEvent("ND:updateLastLocation", function(location) - if not NDCore.player then return end - NDCore.player.lastLocation = location + NDCore.SelectedCharacter.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) \ No newline at end of file diff --git a/client/functions.lua b/client/functions.lua index 3fa06a3..6e2b4a4 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -1,6 +1,19 @@ -function NDCore.GetPlayersFromCoords(distance, coords) +function GetCoreObject() + return NDCore +end + +function NDCore.Functions.GetSelectedCharacter() + return NDCore.SelectedCharacter +end + +function NDCore.Functions.GetCharacters() + return NDCore.Characters +end + + +function NDCore.Functions.GetPlayersFromCoords(distance, coords) if coords then - coords = type(coords) == "table" and vec3(coords.x, coords.y, coords.z) or coords + coords = type(coords) == 'table' and vec3(coords.x, coords.y, coords.z) or coords else coords = GetEntityCoords(PlayerPedId()) end @@ -17,3 +30,51 @@ function NDCore.GetPlayersFromCoords(distance, coords) end return closePlayers end + +-- Callbacks are licensed under LGPL v3.0 +-- +NDCore.callback = {} +local events = {} + +RegisterNetEvent("ND:callbacks", function(key, ...) + local cb = events[key] + return cb and cb(...) +end) + +local function triggerCallback(_, name, cb, ...) + local key = ("%s:%s"):format(name, math.random(0, 100000)) + TriggerServerEvent(("ND:%s_cb"):format(name), key, ...) + + local promise = not cb and promise.new() + + events[key] = function(response, ...) + response = { response, ... } + events[key] = nil + + if promise then + return promise:resolve(response) + end + + if cb then + cb(table.unpack(response)) + end + end + + if promise then + return table.unpack(Citizen.Await(promise)) + end +end + +setmetatable(NDCore.callback, { + __call = triggerCallback +}) + +function NDCore.callback.await(name, ...) + return triggerCallback(nil, name, false, ...) +end + +function NDCore.callback.register(name, callback) + RegisterNetEvent(("ND:%s_cb"):format(name), function(key, ...) + TriggerServerEvent("ND:callbacks", key, callback(...)) + end) +end diff --git a/client/main.lua b/client/main.lua index 1cef364..56d2691 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,36 +1,54 @@ -CreateThread(function() - SetDiscordAppId(Config.discordAppId) - SetDiscordRichPresenceAsset(Config.discordAsset) - SetDiscordRichPresenceAssetSmall(Config.discordAssetSmall) - SetDiscordRichPresenceAction(0, Config.discordActionText, Config.discordActionLink) - SetDiscordRichPresenceAction(1, Config.discordActionText2, Config.discordActionLink2) - 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 presenceTextSmall = ("Playing as: %s %s"):format(NDCore.player.firstname, NDCore.player.lastname) - SetRichPresence(presence) - SetDiscordRichPresenceAssetText(presenceText) - SetDiscordRichPresenceAssetSmallText(presenceTextSmall) - end - Wait(60000) - end -end) +-- For support join my discord: https://discord.gg/Z9Mxu72zZ6 -CreateThread(function() - AddTextEntry("FE_THDR_GTAO", Config.serverName) - local sleep = 500 - while true do - Wait(sleep) - if NDCore.player and IsPauseMenuActive() then - 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)) - EndScaleformMovieMethod() - elseif sleep == 0 then - sleep = 500 +NDCore = {} +NDCore.SelectedCharacter = nil +NDCore.Characters = {} +NDCore.Functions = {} +NDCore.Config = config + +-- discord rich precense will show on a users profile. +if config.enableRichPresence then + Citizen.CreateThread(function() + while true do + if NDCore.SelectedCharacter then + SetDiscordAppId(config.appId) + SetRichPresence(" Playing : " .. config.serverName .. " as " .. NDCore.SelectedCharacter.firstName .. " " .. NDCore.SelectedCharacter.lastName) + SetDiscordRichPresenceAsset(config.largeLogo) + SetDiscordRichPresenceAssetText("Playing: " .. config.serverName) + SetDiscordRichPresenceAssetSmall(config.smallLogo) + SetDiscordRichPresenceAssetSmallText("Playing as: " .. NDCore.SelectedCharacter.firstName .. " " .. NDCore.SelectedCharacter.lastName) + SetDiscordRichPresenceAction(0, config.firstButtonName, config.firstButtonLink) + SetDiscordRichPresenceAction(1, config.secondButtonName, config.secondButtonLink) + end + Citizen.Wait(config.updateIntervall * 1000) end + end) +end + +-- show server name, first name, last name, and the amount of money the character has in the pause menu. +if config.customPauseMenu then + Citizen.CreateThread(function() + while true do + Citizen.Wait(0) + if NDCore.SelectedCharacter then + if IsPauseMenuActive() then + BeginScaleformMovieMethodOnFrontendHeader("SET_HEADING_DETAILS") + AddTextEntry("FE_THDR_GTAO", config.serverName) + ScaleformMovieMethodAddParamPlayerNameString(NDCore.SelectedCharacter.firstName .. " " .. NDCore.SelectedCharacter.lastName) + PushScaleformMovieFunctionParameterString("Cash: $" .. tostring(NDCore.SelectedCharacter.cash)) + PushScaleformMovieFunctionParameterString("Bank: $" .. tostring(NDCore.SelectedCharacter.bank)) + EndScaleformMovieMethod() + end + end + end + end) +end + +-- Enables pvp if it's selected in the config. +AddEventHandler("playerSpawned", function() + if config.enablePVP then + SetCanAttackFriendly(PlayerPedId(), true, false) + NetworkSetFriendlyFireOption(true) end -end) + print("^0This framework is created by ^5Andyyy#7666 ^0for support you can join the ^5discord: ^0https://discord.gg/Z9Mxu72zZ6") +end) \ No newline at end of file diff --git a/config_client.lua b/config_client.lua new file mode 100644 index 0000000..a6e5390 --- /dev/null +++ b/config_client.lua @@ -0,0 +1,74 @@ +-- For support join my discord: https://discord.gg/Z9Mxu72zZ6 + +config = { + serverName = "Andy's Development", + characterLimit = 15, -- How many characters can a player create. + customPauseMenu = true, -- A custom pause menu will display your money, characters name and the server name in the pause menu. + enablePVP = true, -- pvp allows doing damage to other players. + + -- Money related + startingCash = 2500, -- default cash the character will start with if the character creator doesn't specify it. + startingBank = 8000,-- default money in the bank account the character will start with if the character creator doesn't specify it. + + -- If you'd like to whitelist certain roles on discord then set this to true and add role ids. + enableDiscordWhitelist = false, + notWhitelistedMessage = "You're not allowlisted in this server please join our discord to apply for a allowlist: https://discord.gg/Z9Mxu72zZ6", + whitelistRoles = { + "872921520719142932" + }, + + -- These are admins roles that will give a user permission to admin commands and more. + adminRoles = { + "872921520719142932" + }, + + -- Discord Rich presence + enableRichPresence = true, + updateIntervall = 60, -- how many seconds of delay until it updates status. + appId = 858146067018416128, + largeLogo = "andyyy", + smallLogo = "andyyy", + firstButtonName = "DISCORD", + firstButtonLink = "https://discord.gg/Z9Mxu72zZ6", + secondButtonName = "TEBEX", + secondButtonLink = "https://andyyy.tebex.io/category/fivem-scripts", + + + -- Groups can be gangs, jobs, subdivisions, etc. + groups = { + ["Ballas"] = { + "Member", -- rank 1 + "Boss" -- rank 2 + }, + ["SWAT"] = { + "Member", -- rank 1 + "Sniper", -- rank 2 + "Team lead", -- rank 3 + "Commander" -- rank 4 + }, + ["SAHP"] = { + "Trooper", + "Senior Trooper", + "Corporal", + "Sergeant", + "Lieutenant", + "Cheif" + }, + ["LSPD"] = { + "Officer", + "Senior officer", + "Corporal", + "Sergeant", + "Lieutenant", + "Cheif" + }, + ["BCSO"] = { + "Deputy", + "Senior Deputy", + "Corporal", + "Sergeant", + "Lieutenant", + "Cheif" + } + }, +} \ No newline at end of file diff --git a/config_server.lua b/config_server.lua new file mode 100644 index 0000000..7847de3 --- /dev/null +++ b/config_server.lua @@ -0,0 +1,6 @@ +-- For support join my discord: https://discord.gg/Z9Mxu72zZ6 + +server_config = { + discordServerToken = "OTAwODg1MDMxMDQ2Nzc0ODQ1.GU1TSC.Ed8D2_wcU-UQ1vZhe1khahgV8J3hLahOp4N1D8", -- discord bot token for permissions + guildId = "872496972454592523", -- discord guild id +} diff --git a/fxmanifest.lua b/fxmanifest.lua index e084ad9..0069149 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -1,25 +1,39 @@ -- For support join my discord: https://discord.gg/Z9Mxu72zZ6 -author "Andyyy#7666" +author "Andyyy#7666, N1K0#0001" description "ND Framework Core" -version "2.0.0" +version "1.0.3" fx_version "cerulean" game "gta5" lua54 "yes" shared_scripts { - "init.lua" + "config_client.lua", + "shared/main.lua" } client_scripts { + "client/main.lua", "client/functions.lua", "client/events.lua", - "client/main.lua" + "shared/import.lua" } server_scripts { "@oxmysql/lib/MySQL.lua", + "config_server.lua", "server/main.lua", - "server/player.lua" + "server/functions.lua", + "server/events.lua", + "server/commands.lua", + "shared/import.lua" +} + +exports { + "GetCoreObject" +} + +server_exports { + "GetCoreObject" } dependency "oxmysql" diff --git a/init.lua b/init.lua deleted file mode 100644 index b4ded23..0000000 --- a/init.lua +++ /dev/null @@ -1,16 +0,0 @@ -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"), - -} - -NDCore = { - config = Config -} diff --git a/server/commands.lua b/server/commands.lua new file mode 100644 index 0000000..e98e0d5 --- /dev/null +++ b/server/commands.lua @@ -0,0 +1,117 @@ +NDCore.Functions.AddCommand("setmoney", "Admin command, manage player money.", function(source, args, rawCommand) + if not NDCore.Functions.IsPlayerAdmin(source) then return end + + local target = tonumber(args[1]) + local action = args[2] + local moneyType = args[3]:lower() + local amount = tonumber(args[4]) + + if not target or GetPlayerPing(target) == 0 then return end + if action ~= "remove" and action ~= "add" and action ~= "set" then return end + if moneyType ~= "bank" and moneyType ~= "cash" then return end + + if action == "remove" then + if not amount or amount < 1 then return end + NDCore.Functions.DeductMoney(amount, target, moneyType, "Server staff action.") + elseif action == "add" then + if not amount or amount < 1 then return end + NDCore.Functions.AddMoney(amount, target, moneyType, "Server staff action.") + elseif action == "set" then + local character = NDCore.Functions.GetPlayer(target) + NDCore.Functions.SetPlayerData(character.id, moneyType, amount, "Server staff action.") + end +end, true, { + { name="player", help="Player server id" }, + { name="action", help="remove/add/set" }, + { name="type", help="bank/cash" }, + { name="amount" } +}) + +NDCore.Functions.AddCommand("setjob", "Admin command, set player job.", function(source, args, rawCommand) + if not NDCore.Functions.IsPlayerAdmin(source) then + return { + color = {255, 0, 0}, + args = {"Error", "you don't have access to this command."} + } + end + + local target = tonumber(args[1]) + if not target or GetPlayerPing(target) == 0 then + return { + color = {255, 0, 0}, + args = {"Error", "target player not found."} + } + end + + local job = args[2] + if not job then + return { + color = {255, 0, 0}, + args = {"Error", "job required."} + } + end + + local character = NDCore.Functions.GetPlayer(target) + NDCore.Functions.SetPlayerJob(character.id, job, args[3]) + return { + color = {0, 255, 0}, + args = {"Success", GetPlayerName(target) .. " job set to " .. job .. (args[3] and " rank " .. args[3] or " rank 1.")} + } +end, false, { + { name="player", help="Player server id" }, + { name="job name" }, + { name="rank", help="This should be a number, default value is 1." } +}) + +NDCore.Functions.AddCommand("setgroup", "Admin command, set player group.", function(source, args, rawCommand) + if not NDCore.Functions.IsPlayerAdmin(source) then return end + + local target = tonumber(args[1]) + if not target or GetPlayerPing(target) == 0 then + return { + color = {255, 0, 0}, + args = {"Error", "target player not found."} + } + end + + local group = args[3] + if not group then + return { + color = {255, 0, 0}, + args = {"Error", "group required."} + } + end + + local character = NDCore.Functions.GetPlayer(target) + if args[2] == "remove" then + NDCore.Functions.RemovePlayerFromGroup(character.id, group) + return { + color = {0, 255, 0}, + args = {"Success", GetPlayerName(target) .. " removed from " .. group} + } + elseif args[2] == "add" then + local rank = args[4] + NDCore.Functions.SetPlayerToGroup(character.id, group, rank) + return { + color = {0, 255, 0}, + args = {"Success", GetPlayerName(target) .. " added to " .. group .. (rank and " rank " .. rank or " rank 1.")} + } + end +end, false, { + { name="player", help="Player server id" }, + { name="action", help="remove/add"}, + { name="group", help="Group name, make sure it's correct or it won't work."}, + { name="rank", help="This should be a number, default value is 1 (not required if removing)." } +}) + +NDCore.Functions.AddCommand("pay", "give cash to a nearby player", function(source, args, rawCommand) + local amount = args[1] + if not amount or amount == 0 then return end + NDCore.Functions.GiveCashToNearbyPlayer(source, amount) + return { + color = {0, 255, 0}, + args = {"Success", amount .. " paid."} + } +end, true, { + { name="Amount" }, +}) \ No newline at end of file diff --git a/server/events.lua b/server/events.lua new file mode 100644 index 0000000..e8f6251 --- /dev/null +++ b/server/events.lua @@ -0,0 +1,117 @@ +-- For support join my discord: https://discord.gg/Z9Mxu72zZ6 + +-- Check if discord is connected, and if whitelist is enabled then it will only allow you to join if you have the roles. +AddEventHandler("playerConnecting", function(name, setKickReason, deferrals) + local player = source + local discordIdentifier = NDCore.Functions.GetPlayerIdentifierFromType("discord", player) + + deferrals.defer() + Wait(0) + deferrals.update("Connecting to discord.") + Wait(0) + + if not discordIdentifier then + deferrals.done("Your discord isn't connected to FiveM, make sure discord is open and restart FiveM.") + else + if config.enableDiscordWhitelist then + local discordUserId = discordIdentifier:gsub("discord:", "") + local discordInfo = NDCore.Functions.GetUserDiscordInfo(discordUserId) + for _, whitelistRole in pairs(config.whitelistRoles) do + if whitelistRole == 0 or whitelistRole == "0" or (discordInfo and discordInfo.roles[whitelistRole]) then + deferrals.done() + break + end + end + deferrals.done(config.notWhitelistedMessage) + else + deferrals.done() + end + end +end) + +-- Getting all the characters the player has and returning them to the client. +RegisterNetEvent("ND:GetCharacters", function() + local player = source + TriggerClientEvent("ND:returnCharacters", player, NDCore.Functions.GetPlayerCharacters(player)) +end) + +-- Creating a new character. +RegisterNetEvent("ND:newCharacter", function(newCharacter) + local player = source + NDCore.Functions.CreateCharacter(player, newCharacter.firstName, newCharacter.lastName, newCharacter.dob, newCharacter.gender, newCharacter.cash, newCharacter.bank) +end) + +-- Update the character info when edited. +RegisterNetEvent("ND:editCharacter", function(newCharacter) + local player = source + local characters = NDCore.Functions.GetPlayerCharacters(player) + if not characters[newCharacter.id] then return end + NDCore.Functions.UpdateCharacterData(newCharacter.id, newCharacter.firstName, newCharacter.lastName, newCharacter.dob, newCharacter.gender) +end) + +-- Delete character from database. +RegisterNetEvent("ND:deleteCharacter", function(characterId) + local player = source + local characters = NDCore.Functions.GetPlayerCharacters(player) + if not characters[characterId] then return end + NDCore.Functions.DeleteCharacter(characterId) +end) + +-- add a player to the table. +RegisterNetEvent("ND:setCharacterOnline", function(id) + local player = source + local characters = NDCore.Functions.GetPlayerCharacters(player) + if not characters[id] then return end + NDCore.Functions.SetActiveCharacter(player, id) +end) + +-- Update the characters clothes. +RegisterNetEvent("ND:updateClothes", function(clothing) + local player = source + local character = NDCore.Players[player] + NDCore.Functions.SetPlayerData(character.id, "clothing", clothing) +end) + +-- Disconnecting a player +RegisterNetEvent("ND:exitGame", function() + local player = source + DropPlayer(player, "Disconnected.") +end) + +-- Remove player from NDCore.Players table when they leave. +AddEventHandler("playerDropped", function() + local player = source + local character = NDCore.Players[player] + if character then + local ped = GetPlayerPed(player) + local lastLocation = GetEntityCoords(ped) + NDCore.Functions.UpdateLastLocation(character.id, {x = lastLocation.x, y = lastLocation.y, z = lastLocation.z}) + end + TriggerEvent("ND:characterUnloaded", player, character) + character = nil +end) + +-- Get player discord info on join. +AddEventHandler("playerJoining", function() + local src = source + + local discordUserId = NDCore.Functions.GetPlayerIdentifierFromType("discord", src):gsub("discord:", "") + local discordInfo = NDCore.Functions.GetUserDiscordInfo(discordUserId) + + NDCore.PlayersDiscordInfo[src] = discordInfo +end) + +AddEventHandler("onResourceStart", function(resourceName) + if (GetCurrentResourceName() ~= resourceName) then + return + end + Wait(1000) + + if not next(NDCore.PlayersDiscordInfo) then + for _, playerId in ipairs(GetPlayers()) do + local discordUserId = NDCore.Functions.GetPlayerIdentifierFromType("discord", playerId):gsub("discord:", "") + local discordInfo = NDCore.Functions.GetUserDiscordInfo(discordUserId) + NDCore.PlayersDiscordInfo[tonumber(playerId)] = discordInfo + end + end +end) diff --git a/server/functions.lua b/server/functions.lua new file mode 100644 index 0000000..e42e7c9 --- /dev/null +++ b/server/functions.lua @@ -0,0 +1,766 @@ +-- For support join my discord: https://discord.gg/Z9Mxu72zZ6 + +-- Get an active players character data. +function NDCore.Functions.GetPlayer(player) + return NDCore.Players[player] +end + +-- Get all active players character data. +function NDCore.Functions.GetPlayers(getBy, value) + if not getBy or not value then + return NDCore.Players + end + local players = {} + + if getBy == "groups" then + for player, playerInfo in pairs(NDCore.Players) do + if playerInfo.data.groups then + local valueGroup = value:lower() + for group, _ in pairs(playerInfo.data.groups) do + if group and group:lower() == valueGroup then + players[player] = playerInfo + end + end + end + end + else + for player, playerInfo in pairs(NDCore.Players) do + if playerInfo[getBy] == value then + players[player] = playerInfo + end + end + end + + return players +end + +local discordErrors = { + [400] = "improper http request", + [401] = "Discord bot token might be missing or incorrect", + [404] = "user might not be in server.", + [429] = "Discord bot rate limited." +} +-- Used to retrive the players discord server nickname, discord name and tag, and the roles. +function NDCore.Functions.GetUserDiscordInfo(discordUserId) + local data + local timeout = 0 + PerformHttpRequest("https://discordapp.com/api/guilds/" .. server_config.guildId .. "/members/" .. discordUserId, function(errorCode, resultData, resultHeaders) + if errorCode ~= 200 then + print("Error: " .. errorCode .. " " .. discordErrors[errorCode]) + end + local result = json.decode(resultData) + local roles = {} + local nickname = "" + local tag = "" + if result and result.roles then + for _, roleId in pairs(result.roles) do + roles[roleId] = roleId + end + if result.nick then + nickname = result.nick + end + if result.user and result.user.username and result.user.discriminator then + tag = tostring(result.user.username) .. "#" .. tostring(result.user.discriminator) + end + data = { + nickname = nickname, + discordTag = tag, + roles = roles + } + return + end + data = { + nickname = nickname, + discordTag = tag, + roles = roles + } + end, "GET", "", {["Content-Type"] = "application/json", ["Authorization"] = "Bot " .. server_config.discordServerToken}) + while not data do + Wait(1000) + timeout = timeout + 1 + if timeout > 5 then + break + end + end + return data +end + +-- Get player any identifier, available types: steam, license, xbl, ip, discord, live. +function NDCore.Functions.GetPlayerIdentifierFromType(type, player) + local identifierCount = GetNumPlayerIdentifiers(player) + for count = 0, identifierCount do + local identifier = GetPlayerIdentifier(player, count) + if identifier and string.find(identifier, type) then + return identifier + end + end + return nil +end + +-- This will return the server id and ND player data of a nearby player. +function NDCore.Functions.GetNearbyPedToPlayer(player) + local pedCoords = GetEntityCoords(GetPlayerPed(player)) + for targetId, targetInfo in pairs(NDCore.Players) do + local targetCoords = GetEntityCoords(GetPlayerPed(targetId)) + if #(pedCoords - targetCoords) < 2.0 and targetId ~= player then + return targetId, targetInfo + end + end +end + +-- update the players money on the client kinda like a refresh. +function NDCore.Functions.UpdateMoney(player) + local player = tonumber(player) + local result = MySQL.query.await("SELECT cash, bank FROM characters WHERE character_id = ? LIMIT 1", {NDCore.Players[player].id}) + if result then + local cash = result[1].cash + local bank = result[1].bank + NDCore.Players[player].cash = cash + NDCore.Players[player].bank = bank + TriggerClientEvent("ND:updateMoney", player, cash, bank) + end +end + +-- Transfer money from one players bank account to another. +function NDCore.Functions.TransferBank(amount, player, target, descriptionSender, descriptionReceiver) + local amount = tonumber(amount) + local player = tonumber(player) + local target = tonumber(target) + if player == target then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "You can't send money to yourself."} + }) + return false + elseif GetPlayerPing(target) == 0 then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "That player does not exist."} + }) + return false + elseif amount <= 0 then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "You can't send that amount."} + }) + return false + elseif NDCore.Players[player].bank < amount then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "You don't have enough money."} + }) + return false + else + MySQL.query.await("UPDATE characters SET bank = bank - ? WHERE character_id = ?", {amount, NDCore.Players[player].id}) + NDCore.Functions.UpdateMoney(player) + TriggerEvent("ND:moneyChange", player, "bank", amount, "remove", descriptionSender or "Transfer") + TriggerClientEvent("chat:addMessage", player, { + color = {0, 255, 0}, + args = {"Success", "You paid " .. NDCore.Players[target].firstName .. " " .. NDCore.Players[target].lastName .. " $" .. amount .. "."} + }) + + MySQL.query.await("UPDATE characters SET bank = bank + ? WHERE character_id = ?", {amount, NDCore.Players[target].id}) + NDCore.Functions.UpdateMoney(target) + TriggerEvent("ND:moneyChange", target, "bank", amount, "add", descriptionReceiver or "Transfer") + TriggerClientEvent("chat:addMessage", target, { + color = {0, 255, 0}, + args = {"Success", NDCore.Players[player].firstName .. " " .. NDCore.Players[player].lastName .. " sent you $" .. amount .. "."} + }) + return true + end +end + +-- Give cash from one players wallet to another. +function NDCore.Functions.GiveCash(amount, player, target) + local amount = tonumber(amount) + local player = tonumber(player) + local target = tonumber(target) + if player == target then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "You can't give money to yourself."} + }) + return false + elseif GetPlayerPing(target) == 0 then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "That player does not exist."} + }) + return false + elseif amount <= 0 then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "You can't give that amount."} + }) + return false + elseif NDCore.Players[player].cash < amount then + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "You don't have enough money."} + }) + return false + else + MySQL.query.await("UPDATE characters SET cash = cash - ? WHERE character_id = ?", {amount, NDCore.Players[player].id}) + NDCore.Functions.UpdateMoney(player) + TriggerEvent("ND:moneyChange", player, "cash", amount, "remove") + TriggerClientEvent("chat:addMessage", player, { + color = {0, 255, 0}, + args = {"Success", "You gave " .. NDCore.Players[target].firstName .. " " .. NDCore.Players[target].lastName .. " $" .. amount .. "."} + }) + + MySQL.query.await("UPDATE characters SET cash = cash + ? WHERE character_id = ?", {amount, NDCore.Players[target].id}) + NDCore.Functions.UpdateMoney(target) + TriggerEvent("ND:moneyChange", target, "cash", amount, "add") + TriggerClientEvent("chat:addMessage", target, { + color = {0, 255, 0}, + args = {"Success", " Received $" .. amount .. "."} + }) + return true + end +end + +-- Give money from a players wallet to a nearby player. +function NDCore.Functions.GiveCashToNearbyPlayer(player, amount) + local targetId = NDCore.Functions.GetNearbyPedToPlayer(player) + if targetId then + NDCore.Functions.GiveCash(amount, player, targetId) + return true + end + TriggerClientEvent("chat:addMessage", player, { + color = {255, 0, 0}, + args = {"Error", "No players nearby."} + }) + return false +end + +-- withdraws money from a players bank account to their wallet/cash. +function NDCore.Functions.WithdrawMoney(amount, player) + local amount = tonumber(amount) + local player = tonumber(player) + if amount <= 0 then return false end + if NDCore.Players[player].bank < amount then return false end + MySQL.query.await("UPDATE characters SET bank = bank - ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + MySQL.query.await("UPDATE characters SET cash = cash + ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + NDCore.Functions.UpdateMoney(player) + TriggerEvent("ND:moneyChange", player, "bank", amount, "remove", "Withdraw") + TriggerEvent("ND:moneyChange", player, "cash", amount, "add", "Withdraw") + return true +end + +-- deposits money from a players wallet/cash to their bank account. +function NDCore.Functions.DepositMoney(amount, player) + local amount = tonumber(amount) + local player = tonumber(player) + if amount <= 0 then return false end + if NDCore.Players[player].cash < amount then return false end + MySQL.query.await("UPDATE characters SET cash = cash - ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + MySQL.query.await("UPDATE characters SET bank = bank + ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + NDCore.Functions.UpdateMoney(player) + TriggerEvent("ND:moneyChange", player, "cash", amount, "remove", "Deposit") + TriggerEvent("ND:moneyChange", player, "bank", amount, "add", "Deposit") + return true +end + +-- Deducts money from the player, "bank" or "cash" needs to be specified. +function NDCore.Functions.DeductMoney(amount, player, from, description) + local amount = tonumber(amount) + local player = tonumber(player) + if from == "bank" then + MySQL.query.await("UPDATE characters SET bank = bank - ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + elseif from == "cash" then + MySQL.query.await("UPDATE characters SET cash = cash - ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + end + NDCore.Functions.UpdateMoney(player) + TriggerEvent("ND:moneyChange", player, from, amount, "remove", description) +end + +-- Adds money from the player, "bank" or "cash" needs to be specified. +function NDCore.Functions.AddMoney(amount, player, to, description) + local amount = tonumber(amount) + local player = tonumber(player) + if to == "bank" then + MySQL.query.await("UPDATE characters SET bank = bank + ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + elseif to == "cash" then + MySQL.query.await("UPDATE characters SET cash = cash + ? WHERE character_id = ? LIMIT 1", {amount, NDCore.Players[player].id}) + end + NDCore.Functions.UpdateMoney(player) + TriggerEvent("ND:moneyChange", player, to, amount, "add", description) +end + +-- Adds the players character to the NDCore.Players table, this table consists of every players selected character. +function NDCore.Functions.SetActiveCharacter(player, characterId) + if NDCore.Players[player] then + TriggerEvent("ND:characterUnloaded", player, NDCore.Players[player]) + end + local result = MySQL.query.await("SELECT * FROM characters WHERE character_id = ? LIMIT 1", {characterId}) + if result then + local i = result[1] + NDCore.Players[player] = { + source = player, + id = characterId, + firstName = i.first_name, + lastName = i.last_name, + dob = i.dob, + gender = i.gender, + cash = i.cash, + bank = i.bank, + phoneNumber = i.phone_number, + lastLocation = json.decode(i.last_location), + inventory = json.decode(i.inventory), + discordInfo = NDCore.PlayersDiscordInfo[player], + data = json.decode(i.data), + job = i.job + } + end + NDCore.Functions.RefreshCommands(player) + TriggerEvent("ND:characterLoaded", NDCore.Players[player]) + TriggerClientEvent("ND:setCharacter", player, NDCore.Players[player]) +end + +-- This returns all the characters the player has. +function NDCore.Functions.GetPlayerCharacters(player) + local characters = {} + local result = MySQL.query.await("SELECT * FROM characters WHERE license = ?", {NDCore.Functions.GetPlayerIdentifierFromType("license", player)}) + for i = 1, #result do + local temp = result[i] + characters[temp.character_id] = { + id = temp.character_id, + firstName = temp.first_name, + lastName = temp.last_name, + dob = temp.dob, + gender = temp.gender, + cash = temp.cash, + bank = temp.bank, + phoneNumber = temp.phone_number, + lastLocation = json.decode(temp.last_location), + inventory = json.decode(temp.inventory), + discordInfo = NDCore.PlayersDiscordInfo[player], + data = json.decode(temp.data), + job = temp.job + } + end + return characters +end + +-- Creates a new character for the player and returns all their characters to the client. +function NDCore.Functions.CreateCharacter(player, firstName, lastName, dob, gender, cb) + local characterId = false + local license = NDCore.Functions.GetPlayerIdentifierFromType("license", player) + local result = MySQL.query.await("SELECT character_id FROM characters WHERE license = ?", {license}) + if result and config.characterLimit > #result then + characterId = MySQL.insert.await("INSERT INTO characters (license, first_name, last_name, dob, gender, cash, bank, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", {license, firstName, lastName, dob, gender, config.startingCash, config.startingBank, json.encode({groups={}})}) + if cb then cb(characterId) end + TriggerClientEvent("ND:returnCharacters", player, NDCore.Functions.GetPlayerCharacters(player)) + end + return characterId +end + +-- Update/edit a character info by character id. +function NDCore.Functions.UpdateCharacter(characterId, firstName, lastName, dob, gender) + local result = MySQL.query.await("UPDATE characters SET first_name = ?, last_name = ?, dob = ?, gender = ? WHERE character_id = ? LIMIT 1", {firstName, lastName, dob, gender, characterId}) + return result +end + +-- Delete a character by character id. +function NDCore.Functions.DeleteCharacter(characterId) + local result = MySQL.query.await("DELETE FROM characters WHERE character_id = ? LIMIT 1", {characterId}) + return result +end + +-- Updates the player's data +function NDCore.Functions.SetPlayerData(characterId, key, value, description) + if not key then return end + + local player = nil + for id, character in pairs(NDCore.Players) do + if character.id == characterId then + player = id + break + end + end + + if key == "cash" then + if player then + NDCore.Players[player][key] = value + TriggerEvent("ND:moneyChange", player, "cash", tonumber(value), "set", description) + end + MySQL.query.await("UPDATE characters SET cash = ? WHERE character_id = ?", {tonumber(value), characterId}) + elseif key == "bank" then + if player then + NDCore.Players[player][key] = value + TriggerEvent("ND:moneyChange", player, "bank", tonumber(value), "set", description) + end + MySQL.query.await("UPDATE characters SET bank = ? WHERE character_id = ?", {tonumber(value), characterId}) + elseif key == "job" then + if player then + NDCore.Players[player].job = value + end + MySQL.query.await("UPDATE characters SET job = ? WHERE character_id = ?", {value, characterId}) + else + if player then + NDCore.Players[player].data[key] = value + MySQL.query.await("UPDATE characters SET `data` = ? WHERE character_id = ?", {json.encode(NDCore.Players[player].data), characterId}) + else + local result = MySQL.query.await("SELECT `data` FROM characters WHERE character_id = ?", {characterId}) + if not result or not result[1] then return end + + local data = json.decode(result[1].data) + data[key] = value + MySQL.query.await("UPDATE characters SET `data` = ? WHERE character_id = ?", {json.encode(data), characterId}) + end + end + + if not player then return end + TriggerClientEvent("ND:updateCharacter", player, NDCore.Players[player]) +end + +-- Get a character by the character id. +function NDCore.Functions.GetPlayerByCharacterId(id) + for _, character in pairs(NDCore.Players) do + if character.id == id then + return character + end + end +end + +-- Generate a random string with letters and numbers. +function randomString(length) + local number = {} + for i = 1, length do + number[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) + end + return table.concat(number) +end + +-- Give a player a license. +function NDCore.Functions.CreatePlayerLicense(characterId, licenseType, expire) + local expireIn = tonumber(expire) + if not expireIn then + expireIn = 2592000 + end + + local time = os.time() + local license = { + type = licenseType, + status = "valid", + issued = time, + expires = time+expireIn, + identifier = randomString(16) + } + + local character = NDCore.Functions.GetPlayerByCharacterId(characterId) + if character then + local data = character.data + if not data.licences then + data.licences = {} + end + character.data.licences[#character.data.licences+1] = license + NDCore.Functions.SetPlayerData(character.id, "licences", character.data.licences) + return true + end + + local result = MySQL.query.await("SELECT data FROM characters WHERE character_id = ?", {characterId}) + if result and result[1] then + local data = result[1].data + if not data.licences then + data.licences = {} + end + data.licences[#data.licences+1] = license + NDCore.Functions.SetPlayerData(character.id, "licences", data.licences) + return true + end +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 + end + end + return {} +end + +-- Edit a license by the license identifier. +function NDCore.Functions.EditPlayerLicense(characterId, identifier, newData) + local licences = {} + local character = NDCore.Functions.GetPlayerByCharacterId(characterId) + if character then + licences = character.data.licences + else + local result = MySQL.query.await("SELECT data FROM characters WHERE character_id = ?", {characterId}) + if result and result[1] then + local data = result[1].data + if not data.licences then + data.licences = {} + end + licences = data.licences + end + end + + local license = NDCore.Functions.FindLicenseByIdentifier(licences, identifier) + for k, v in pairs(newData) do + license[k] = v + end + NDCore.Functions.SetPlayerData(characterId, "licences", licences) + return licences +end + +-- Set the players job and job rank. +function NDCore.Functions.SetPlayerJob(characterId, job, rank) + if not job then return end + + local jobRank = tonumber(rank) + if not jobRank then + jobRank = 1 + end + + local result = MySQL.query.await("SELECT job FROM characters WHERE character_id = ?", {characterId}) + if result and result[1] then + local character = NDCore.Functions.GetPlayerByCharacterId(characterId) + if character then + local oldRank = 1 + if character.data.groups and character.data.groups[character.job] then + oldRank = character.data.groups[character.job].rank + end + TriggerEvent("ND:jobChanged", character.source, {name = job, rank = jobRank}, {name = character.job, rank = oldRank}) + TriggerClientEvent("ND:jobChanged", character.source, {name = job, rank = jobRank}, {name = character.job, rank = oldRank}) + end + NDCore.Functions.RemovePlayerFromGroup(characterId, result[1].job) + end + + NDCore.Functions.SetPlayerData(characterId, "job", job) + NDCore.Functions.SetPlayerToGroup(characterId, job, jobRank) +end + +-- Set a player to a group. +function NDCore.Functions.SetPlayerToGroup(characterId, group, rank) + local groupRank = tonumber(rank) + if not groupRank then + groupRank = 1 + end + + local group = group:lower() + for groupName, groupRanks in pairs(config.groups) do + if groupName:lower() == group then + group = groupName + break + end + end + + local character = NDCore.Functions.GetPlayerByCharacterId(characterId) + if character then + local data = character.data + if not data.groups then + data.groups = {} + end + local rankName = tostring(groupRank) + if config.groups[group] and config.groups[group][groupRank] then + rankName = config.groups[group][groupRank] + end + data.groups[group] = { + rank = groupRank, + rankName = rankName + } + NDCore.Functions.SetPlayerData(characterId, "groups", data.groups) + return true + end + + local result = MySQL.query.await("SELECT data FROM characters WHERE character_id = ?", {characterId}) + if not result or not result[1] then return end + local data = json.decode(result[1].data) + if not data then + data = {} + end + if not data.groups then + data.groups = {} + end + local rankName = tostring(groupRank) + if config.groups[group] and config.groups[group][groupRank] then + rankName = config.groups[group][groupRank] + end + data.groups[group] = { + rank = groupRank, + rankName = rankName + } + NDCore.Functions.SetPlayerData(characterId, "groups", data.groups) + return true +end + +-- Remove a player from a group. +function NDCore.Functions.RemovePlayerFromGroup(characterId, group) + if not group then return end + local group = group:lower() + for groupName, groupRanks in pairs(config.groups) do + if groupName:lower() == group then + group = groupName + break + end + end + + local character = NDCore.Functions.GetPlayerByCharacterId(characterId) + if character then + local data = character.data + if not data.groups then + data.groups = {} + end + data.groups[group] = nil + NDCore.Functions.SetPlayerData(characterId, "groups", data.groups) + return true + end + + local result = MySQL.query.await("SELECT data FROM characters WHERE character_id = ?", {characterId}) + if result and result[1] then + local data = result[1].data + if not data.groups then + data.groups = {} + end + data.groups[group] = nil + NDCore.Functions.SetPlayerData(characterId, "groups", data.groups) + return true + end +end + +-- Update the characters last location into the database. +function NDCore.Functions.UpdateLastLocation(characterId, location) + local result = MySQL.query.await("UPDATE characters SET last_location = ? WHERE character_id = ? LIMIT 1", {json.encode(location), characterId}) + return result +end + +function NDCore.Functions.AddCommand(name, help, callback, argsrequired, arguments) + local commandName = name:lower() + if NDCore.Commands[commandName] then print("/" .. commandName .. " has already been registered.") return end + + local arguments = arguments or {} + + RegisterCommand(commandName, function(source, args, rawCommand) + if argsrequired and #args < #arguments then + return TriggerClientEvent("chat:addMessage", source, { + color = {255, 0, 0}, + multiline = true, + args = {"Error", "all arguments required."} + }) + end + local message = callback(source, args, rawCommand) + if not message then return end + TriggerClientEvent("chat:addMessage", source, message) + end, false) + + NDCore.Commands[commandName] = { + name = commandName, + help = help, + callback = callback, + argsrequired = argsrequired, + arguments = arguments + } +end + +function NDCore.Functions.RefreshCommands(source) + local suggestions = {} + for command, info in pairs(NDCore.Commands) do + suggestions[#suggestions + 1] = { + name = "/" .. command, + help = info.help, + params = info.arguments + } + end + TriggerClientEvent("chat:addSuggestions", source, suggestions) +end + +function NDCore.Functions.IsPlayerAdmin(src) + local discordInfo = NDCore.PlayersDiscordInfo[src] + if not discordInfo or not discordInfo.roles then return end + for _, adminRole in pairs(config.adminRoles) do + for _, role in pairs(discordInfo.roles) do + if role == adminRole then return true end + end + end +end + +function NDCore.Functions.VersionChecker(expectedResourceName, resourceName, downloadLink, rawGithubLink) + if expectedResourceName ~= resourceName then + print(("^4%s ^1WARNING^0"):format(expectedResourceName)) + print(("Change the resource name to ^4%s ^0or else it won't work properly!"):format(expectedResourceName)) + StopResource(resourceName) + return + end + PerformHttpRequest(rawGithubLink, function(errorCode, resultData, resultHeaders) + local i, j = tostring(resultData):find("version") + if not i or not j then return end + local resultData = tostring(resultData):sub(i, j + 12) + local resultData = resultData:gsub("version \"", "") + local i, j = resultData:find("\"") + local resultData = resultData:sub(1, i - 1) + local githubVersion = resultData:gsub("%.", "") + local fileVersion = GetResourceMetadata(expectedResourceName, "version", 0):gsub("%.", "") + + if not githubVersion and not fileVersion then + print(("^4%s ^1WARNING^0"):format(expectedResourceName)) + print(("You may not have the latest version of ^4%s^0. A newer, improved version may be present at ^5%s^0"):format(expectedResourceName, downloadLink)) + elseif githubVersion > fileVersion then + local oldVersion = ("%s.%s.%s"):format(fileVersion:sub(1, 1), fileVersion:sub(2, 2), fileVersion:sub(3, 3)) + local newVersion = ("%s.%s.%s"):format(githubVersion:sub(1, 1), githubVersion:sub(2, 2), githubVersion:sub(3, 3)) + print(("^4%s ^1WARNING^0"):format(expectedResourceName)) + print(("^4%s ^0is outdated. Please update it from ^5%s ^0| Current Version: ^1%s ^0| New Version: ^2%s ^0|"):format(expectedResourceName, downloadLink, oldVersion, newVersion)) + elseif githubVersion < fileVersion then + local oldVersion = ("%s.%s.%s"):format(fileVersion:sub(1, 1), fileVersion:sub(2, 2), fileVersion:sub(3, 3)) + local newVersion = ("%s.%s.%s"):format(githubVersion:sub(1, 1), githubVersion:sub(2, 2), githubVersion:sub(3, 3)) + print(("^4%s ^1WARNING^0"):format(expectedResourceName)) + print(("^4%s ^0version number is higher than expected | Current Version: ^3%s ^0| Expected Version: ^2%s ^0|"):format(expectedResourceName, oldVersion, newVersion)) + else + local newVersion = ("%s.%s.%s"):format(githubVersion:sub(1, 1), githubVersion:sub(2, 2), githubVersion:sub(3, 3)) + print(("^4%s ^0is up to date | Current Version: ^2%s ^0|"):format(expectedResourceName, newVersion)) + end + end) +end +NDCore.Functions.VersionChecker("ND_Core", GetCurrentResourceName(), "https://github.com/ND-Framework/ND_Core", "https://raw.githubusercontent.com/ND-Framework/ND_Core/main/fxmanifest.lua") + + +-- Callbacks are licensed under LGPL v3.0 +-- +NDCore.callback = {} +local events = {} + +RegisterNetEvent("ND:callbacks", function(key, ...) + local cb = events[key] + return cb and cb(...) +end) + +function triggerCallback(_, name, playerId, cb, ...) + local key = ("%s:%s:%s"):format(name, math.random(0, 100000), playerId) + TriggerClientEvent(("ND:%s_cb"):format(name), playerId, key, ...) + + local promise = not cb and promise.new() + + events[key] = function(response, ...) + response = { response, ... } + events[key] = nil + + if promise then + return promise:resolve(response) + end + + if cb then + cb(table.unpack(response)) + end + end + + if promise then + return table.unpack(Citizen.Await(promise)) + end +end + +setmetatable(NDCore.callback, { + __call = triggerCallback +}) + +function NDCore.callback.await(name, playerId, ...) + return triggerCallback(nil, name, playerId, false, ...) +end + +function NDCore.callback.register(name, callback) + RegisterNetEvent(("ND:%s_cb"):format(name), function(key, ...) + local src = source + TriggerClientEvent("ND:callbacks", src, key, callback(src, ...)) + end) +end diff --git a/server/main.lua b/server/main.lua index d13951d..f84ab27 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,14 +1,21 @@ -ActivePlayers = {} +-- For support join my discord: https://discord.gg/Z9Mxu72zZ6 -function NDCore.getPlayer(src) - return ActivePlayers[src] +NDCore = {} +NDCore.Players = {} +NDCore.Functions = {} +NDCore.Commands = {} +NDCore.PlayersDiscordInfo = {} +NDCore.Config = config + +function GetCoreObject() + return NDCore end -AddEventHandler("playerDropped", function() - local src = source - local char = ActivePlayers[src] - if not char then return end - char:unload() +isResourceStarted("ox_inventory", function(started) + if not started then return end + SetConvarReplicated("inventory:framework", "nd") end) -SetConvarServerInfo("ND_Core", GetResourceMetadata(GetCurrentResourceName(), "version", 0) or "invalid") +for _, roleid in pairs(config.adminRoles) do + ExecuteCommand("add_principal identifier.discord:" .. roleid .. " group.admin") +end \ No newline at end of file diff --git a/shared/import.lua b/shared/import.lua new file mode 100644 index 0000000..138e3c9 --- /dev/null +++ b/shared/import.lua @@ -0,0 +1 @@ +NDCore = exports["ND_Core"]:GetCoreObject() \ No newline at end of file diff --git a/shared/main.lua b/shared/main.lua new file mode 100644 index 0000000..948dd79 --- /dev/null +++ b/shared/main.lua @@ -0,0 +1,30 @@ +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 isResourceStarted(resourceName, cb) + local started = GetResourceState(resourceName) == "started" + startedResources[resourceName] = started + + if cb then + callbacks[resourceName] = cb + cb(started) + end + + return started +end + +exports("isResourceStarted", isResourceStarted) \ No newline at end of file From 56e0b2f4fd57b016082c70fb72b57e95f9c251e6 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Mon, 10 Jul 2023 02:42:01 +0200 Subject: [PATCH 5/7] Delete player.lua --- server/player.lua | 207 ---------------------------------------------- 1 file changed, 207 deletions(-) delete mode 100644 server/player.lua diff --git a/server/player.lua b/server/player.lua deleted file mode 100644 index 952d877..0000000 --- a/server/player.lua +++ /dev/null @@ -1,207 +0,0 @@ -local function charDelete(self) - 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 - -local function charDeductMoney(self, 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 - -local function charAddMoney(self, 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 - -local function charDepositMoney(self, 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") -end - -local function charWithdrawMoney(self, 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") -end - -local function charSetMetadata(self, key, value) - self.metadata[key] = value - if self.source then - ActivePlayers[self.source] = self - return ActivePlayers[self.source].metadata - end - return self.metadata -end - -local function charUnload(self) - TriggerEvent("ND:characterUnloaded", self.source, self) - local ped = GetPlayerPed(self.source) - self:setMetadata("location", GetEntityCoords(ped)) - self:save() - if not self.source then return end - ActivePlayers[self.source] = nil -end - -local function charSave(self) - MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, metadata = ?, inventory = ? WHERE charid = ?", { - self.name, - self.firstname, - self.lastname, - self.dob, - self.gender, - self.cash, - self.bank, - json.encode(self.metadata), - json.encode(self.inventory), - self.id, - }) -end - -local function charCreateLicense(self, licenseType, expire) - local expireIn = tonumber(expire) or 2592000 - local time = os.time() - local licenses = self.metadata.licenses - local identifier = {} - - for i=1, 16 do - identifier[i] = math.random(0, 1) == 1 and string.char(math.random(65, 90)) or math.random(0, 9) - end - - local license = { - type = licenseType, - status = "valid", - issued = time, - expires = time+expireIn, - identifier = table.concat(identifier) - } - - if licenses then - self.metadata.licenses[#licenses+1] = license - return - end - self.metadata.licenses = {license} - - if not self.source then return end - ActivePlayers[self.source] = self -end -end - -local function initPlayerTable(playerTable) - playerTable.delete = charDelete, - playerTable.deductMoney = charDeductMoney, - playerTable.addMoney = charAddMoney - playerTable.depositMoney = charDepositMoney, - playerTable.withdrawMoney = charWithdrawMoney, - playerTable.setMetadata = charSetMetadata, - playerTable.save = charSave, - playerTable.unload = charUnload, - playerTable.createLicense = charCreateLicense - return playerTable -end - -function NDCore.newCharacter(src, info) - local license = GetPlayerIdentifierByType(src, "license") - if not license then return end - - local charInfo = initPlayerTable({ - source = src, - name = GetPlayerName(src) or "", - firstname = info.firstname or "", - lastname = info.lastname or "", - dob = info.dob or "", - gender = info.gender or "", - cash = info.cash or 0, - bank = info.bank or 0, - license = license, - metadata = info.metadata or {}, - 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.name, - charInfo.firstname, - charInfo.lastname, - charInfo.dob, - charInfo.gender, - charInfo.cash, - charInfo.bank, - charInfo.metadata - ) - - return charInfo -end - -function NDCore.fetchCharacter(id) - local result = MySQL.query.await("SELECT * FROM nd_characters WHERE charid = ?", {id}) - if not result then return end - - local info = result[1] - return initPlayerTable({ - id = id, - name = info.name, - firstname = info.firstname, - lastname = info.lastname, - dob = info.dob, - gender = info.gender, - cash = info.cash, - bank = info.bank, - license = info.license, - metadata = json.decode(info.metadata), - inventory = json.decode(i.inventory) - }) -end - -function NDCore.getPlayerCharacters(src) - local characters = {} - local result = MySQL.query.await("SELECT * FROM nd_characters WHERE license = ?", {GetPlayerIdentifierByType(src, "license")}) - local amount = #result - - for i=1, amount do - local info = result[i] - characters[info.charid] = initPlayerTable({ - id = info.charid, - name = info.name, - firstname = info.firstname, - lastname = info.lastname, - dob = info.dob, - gender = info.gender, - cash = info.cash, - bank = info.bank, - license = info.license, - metadata = json.decode(info.metadata), - inventory = json.decode(info.inventory), - }) - end - return characters -end - -function NDCore.setActiveCharacter(src, id) - local char = ActivePlayers[src] - if char then char:unload() 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) - return ActivePlayers[src] -end From 38ac145a88d7763bda26742f415f4861bfee06d6 Mon Sep 17 00:00:00 2001 From: Andy <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 29 Aug 2023 03:39:23 +0200 Subject: [PATCH 6/7] 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? [![Discord](https://discordapp.com/api/guilds/857672921912836116/widget.png?style=banner3)](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 7882b2235ecdf53414196944493563e4cdbaa309 Mon Sep 17 00:00:00 2001 From: Andyyy7666 <86536434+Andyyy7666@users.noreply.github.com> Date: Tue, 29 Aug 2023 03:40:32 +0200 Subject: [PATCH 7/7] Revert "Update README.md" This reverts commit 38ac145a88d7763bda26742f415f4861bfee06d6. --- README.md | 103 ++++++++---------------------------------------------- 1 file changed, 14 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 209e72e..4039f51 100644 --- a/README.md +++ b/README.md @@ -7,97 +7,22 @@

Documentation ## Dependency: -* [oxmysql](https://github.com/overextended/oxmysql/releases) -* [ox_lib](https://github.com/overextended/ox_lib/releases) +[oxmysql](https://github.com/overextended/oxmysql/releases/download/v2.4.0/oxmysql.zip) ## Addons -* [Character Selection](https://github.com/ND-Framework/ND_Characters/tree/wip-v2) + +* [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) # Need support? [![Discord](https://discordapp.com/api/guilds/857672921912836116/widget.png?style=banner3)](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"] - } -} -```