diff --git a/resources/[inventory]/nordi_vending/client.lua b/resources/[inventory]/nordi_vending/client.lua index a02786216..ee0d582e2 100644 --- a/resources/[inventory]/nordi_vending/client.lua +++ b/resources/[inventory]/nordi_vending/client.lua @@ -26,51 +26,55 @@ function DrawText3D(x, y, z, text) ClearDrawOrigin() end --- Cache machine data to avoid constant callbacks +-- Get machine data with proper callback handling function GetMachineData(entity) - local entityId = NetworkGetNetworkIdFromEntity(entity) + local coords = GetEntityCoords(entity) + local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z) - if not machineData[entityId] then - local coords = GetEntityCoords(entity) - local isRegistered = false - local canManage = false + -- Check if we need to refresh the data + local currentTime = GetGameTimer() + if not machineData[entityId] or (currentTime - machineData[entityId].lastCheck > 10000) then + -- Initialize with default values + machineData[entityId] = { + isRegistered = false, + canManage = false, + lastCheck = currentTime, + checking = true + } -- Check if machine is registered QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists) - isRegistered = exists - - -- Only check management if registered - if isRegistered then + if exists then + machineData[entityId].isRegistered = true + + -- Only check management if registered QBCore.Functions.TriggerCallback('vending:server:canManage', function(result) - canManage = result - - -- Store data in cache - machineData[entityId] = { - isRegistered = isRegistered, - canManage = canManage, - lastCheck = GetGameTimer() - } + machineData[entityId].canManage = result + machineData[entityId].checking = false end, coords) else - -- Store data in cache - machineData[entityId] = { - isRegistered = false, - canManage = false, - lastCheck = GetGameTimer() - } + machineData[entityId].isRegistered = false + machineData[entityId].canManage = false + machineData[entityId].checking = false end end, coords) - - -- Wait for callbacks to complete + end + + -- Wait for callbacks to complete if currently checking + if machineData[entityId].checking then local timeout = 0 - while not machineData[entityId] and timeout < 50 do + while machineData[entityId].checking and timeout < 50 do Wait(10) timeout = timeout + 1 end + + -- If timeout reached, set checking to false to avoid deadlock + if timeout >= 50 then + machineData[entityId].checking = false + end end - -- Return cached data or default - return machineData[entityId] or {isRegistered = false, canManage = false} + return machineData[entityId] end -- Clear cache periodically @@ -110,7 +114,7 @@ CreateThread(function() foundMachine = true nearbyMachine = obj - -- Get machine data from cache + -- Get machine data local data = GetMachineData(obj) -- Display appropriate text based on machine status @@ -177,9 +181,6 @@ RegisterNetEvent('vending:client:refreshTargets', function() machineData = {} end) --- Keep all the existing event handlers and functions below this point --- They don't need to be modified since they work with ox_lib - -- Buy vending machine RegisterNetEvent('vending:client:buyMachine', function(data) local entity = data.entity @@ -207,6 +208,9 @@ RegisterNetEvent('vending:client:buyMachine', function(data) icon = 'fas fa-check', onSelect = function() TriggerServerEvent('vending:server:registerMachine', coords, prop) + -- Clear cache for this machine + local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z) + machineData[entityId] = nil end }, { @@ -225,41 +229,49 @@ RegisterNetEvent('vending:client:openBuyMenu', function(data) local entity = data.entity local coords = GetEntityCoords(entity) - QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items) - if #items == 0 then - QBCore.Functions.Notify('Dieser Automat ist leer!', 'error') + -- Double-check if machine is registered before proceeding + QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists) + if not exists then + QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error') return end - local options = {} - - for i = 1, #items do - local item = items[i] - if item.amount > 0 then - local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name - table.insert(options, { - title = itemLabel, - description = 'Preis: $' .. item.price .. ' | Verfügbar: ' .. item.amount, - icon = 'fas fa-shopping-cart', - onSelect = function() - openQuantityDialog(coords, item.name, item.price, item.amount, itemLabel) - end - }) + QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items) + if #items == 0 then + QBCore.Functions.Notify('Dieser Automat ist leer!', 'error') + return end - end - - if #options == 0 then - QBCore.Functions.Notify('Keine Artikel verfügbar!', 'error') - return - end - - lib.registerContext({ - id = 'vending_buy_menu', - title = 'Verkaufsautomat', - options = options - }) - - lib.showContext('vending_buy_menu') + + local options = {} + + for i = 1, #items do + local item = items[i] + if item.amount > 0 then + local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name + table.insert(options, { + title = itemLabel, + description = 'Preis: $' .. item.price .. ' | Verfügbar: ' .. item.amount, + icon = 'fas fa-shopping-cart', + onSelect = function() + openQuantityDialog(coords, item.name, item.price, item.amount, itemLabel) + end + }) + end + end + + if #options == 0 then + QBCore.Functions.Notify('Keine Artikel verfügbar!', 'error') + return + end + + lib.registerContext({ + id = 'vending_buy_menu', + title = 'Verkaufsautomat', + options = options + }) + + lib.showContext('vending_buy_menu') + end, coords) end, coords) end) @@ -292,76 +304,84 @@ RegisterNetEvent('vending:client:openOwnerMenu', function(data) local entity = data.entity local coords = GetEntityCoords(entity) - QBCore.Functions.TriggerCallback('vending:server:getMachineByCoords', function(machine) - if not machine then - QBCore.Functions.Notify('Automat nicht gefunden!', 'error') + -- Double-check if player can manage this machine before proceeding + QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage) + if not canManage then + QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') return end - local options = { - { - title = 'Inventar verwalten', - description = 'Items hinzufügen/entfernen', - icon = 'fas fa-box', - onSelect = function() - TriggerServerEvent('vending:server:openStash', coords) - end - }, - { - title = 'Preise festlegen', - description = 'Verkaufspreise für Items setzen', - icon = 'fas fa-tags', - onSelect = function() - openPriceMenu(coords) - end - }, - { - title = 'Geld abheben', - description = 'Verfügbar: $' .. machine.money, - icon = 'fas fa-money-bill', - onSelect = function() - openWithdrawMenu(coords, machine.money) - end - }, - { - title = 'Statistiken', - description = 'Verkaufsstatistiken anzeigen', - icon = 'fas fa-chart-bar', - onSelect = function() - openStatsMenu(machine) - end + QBCore.Functions.TriggerCallback('vending:server:getMachineByCoords', function(machine) + if not machine then + QBCore.Functions.Notify('Automat nicht gefunden!', 'error') + return + end + + local options = { + { + title = 'Inventar verwalten', + description = 'Items hinzufügen/entfernen', + icon = 'fas fa-box', + onSelect = function() + TriggerServerEvent('vending:server:openStash', coords) + end + }, + { + title = 'Preise festlegen', + description = 'Verkaufspreise für Items setzen', + icon = 'fas fa-tags', + onSelect = function() + openPriceMenu(coords) + end + }, + { + title = 'Geld abheben', + description = 'Verfügbar: $' .. machine.money, + icon = 'fas fa-money-bill', + onSelect = function() + openWithdrawMenu(coords, machine.money) + end + }, + { + title = 'Statistiken', + description = 'Verkaufsstatistiken anzeigen', + icon = 'fas fa-chart-bar', + onSelect = function() + openStatsMenu(machine) + end + } } - } - - -- Add manager options only for owner - if machine.isOwner then - table.insert(options, { - title = 'Verwalter', - description = 'Verwalter hinzufügen/entfernen', - icon = 'fas fa-users-cog', - onSelect = function() - openManagersMenu(coords) - end + + -- Add manager options only for owner + if machine.isOwner then + table.insert(options, { + title = 'Verwalter', + description = 'Verwalter hinzufügen/entfernen', + icon = 'fas fa-users-cog', + onSelect = function() + openManagersMenu(coords) + end + }) + + -- Add sell option only for owner + table.insert(options, { + title = 'Automaten verkaufen', + description = 'Verkaufe den Automaten für ' .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. '$', + icon = 'fas fa-dollar-sign', + onSelect = function() + sellVendingMachine(coords, machine.id) + end + }) + end + + lib.registerContext({ + id = 'vending_owner_menu', + title = 'Verkaufsautomat Verwaltung', + options = options }) - -- Add sell option only for owner - table.insert(options, { - title = 'Automaten verkaufen', - description = 'Verkaufe den Automaten für ' .. math.floor(Config.VendingMachinePrice * Config.SellBackPercentage / 100) .. '$', - icon = 'fas fa-dollar-sign', - onSelect = function() - sellVendingMachine(coords, machine.id) - end - }) - end - - lib.registerContext({ - id = 'vending_owner_menu', - title = 'Verkaufsautomat Verwaltung', - options = options - }) - - lib.showContext('vending_owner_menu') + lib.showContext('vending_owner_menu') + end, coords) end, coords) end) @@ -378,40 +398,51 @@ function sellVendingMachine(coords, machineId) if input and input[1] then TriggerServerEvent('vending:server:sellMachine', coords, machineId) + -- Clear cache for this machine + local entityId = tostring(coords.x) .. tostring(coords.y) .. tostring(coords.z) + machineData[entityId] = nil end end -- Open price menu function openPriceMenu(coords) - QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items) - if #items == 0 then - QBCore.Functions.Notify('Keine Items im Automaten!', 'error') + -- Double-check if player can manage this machine before proceeding + QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage) + if not canManage then + QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') return end - local options = {} - - for i = 1, #items do - local item = items[i] - local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name - table.insert(options, { - title = itemLabel, - description = 'Aktueller Preis: $' .. item.price, - icon = 'fas fa-tag', - onSelect = function() - setPriceForItem(coords, item.name, itemLabel) - end + QBCore.Functions.TriggerCallback('vending:server:getStashItems', function(items) + if #items == 0 then + QBCore.Functions.Notify('Keine Items im Automaten!', 'error') + return + end + + local options = {} + + for i = 1, #items do + local item = items[i] + local itemLabel = QBCore.Shared.Items[item.name] and QBCore.Shared.Items[item.name].label or item.name + table.insert(options, { + title = itemLabel, + description = 'Aktueller Preis: $' .. item.price, + icon = 'fas fa-tag', + onSelect = function() + setPriceForItem(coords, item.name, itemLabel) + end + }) + end + + lib.registerContext({ + id = 'vending_price_menu', + title = 'Preise festlegen', + menu = 'vending_owner_menu', + options = options }) - end - - lib.registerContext({ - id = 'vending_price_menu', - title = 'Preise festlegen', - menu = 'vending_owner_menu', - options = options - }) - - lib.showContext('vending_price_menu') + + lib.showContext('vending_price_menu') + end, coords) end, coords) end @@ -435,25 +466,33 @@ end -- Open withdraw menu function openWithdrawMenu(coords, availableMoney) - if availableMoney <= 0 then - QBCore.Functions.Notify('Kein Geld im Automaten!', 'error') - return - end - - local input = lib.inputDialog('Geld abheben', { - { - type = 'number', - label = 'Betrag (Verfügbar: $' .. availableMoney .. ')', - description = 'Wie viel möchtest du abheben?', - required = true, - min = 1, - max = availableMoney - } - }) - - if input and input[1] then - TriggerServerEvent('vending:server:withdrawMoney', coords, tonumber(input[1])) - end + -- Double-check if player can manage this machine before proceeding + QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage) + if not canManage then + QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') + return + end + + if availableMoney <= 0 then + QBCore.Functions.Notify('Kein Geld im Automaten!', 'error') + return + end + + local input = lib.inputDialog('Geld abheben', { + { + type = 'number', + label = 'Betrag (Verfügbar: $' .. availableMoney .. ')', + description = 'Wie viel möchtest du abheben?', + required = true, + min = 1, + max = availableMoney + } + }) + + if input and input[1] then + TriggerServerEvent('vending:server:withdrawMoney', coords, tonumber(input[1])) + end + end, coords) end -- Open stats menu @@ -486,102 +525,118 @@ end -- Open managers menu function openManagersMenu(coords) - -- Get current managers - QBCore.Functions.TriggerCallback('vending:server:getManagers', function(managers) - local options = { - { - title = 'Verwalter hinzufügen', - description = 'Neuen Verwalter hinzufügen', - icon = 'fas fa-user-plus', - onSelect = function() - openAddManagerMenu(coords) - end - } - } - - -- Add existing managers with remove option - if #managers > 0 then - for i = 1, #managers do - local manager = managers[i] - table.insert(options, { - title = manager.name, - description = manager.online and 'Online' or 'Offline', - icon = manager.online and 'fas fa-circle text-success' or 'fas fa-circle text-danger', - onSelect = function() - lib.registerContext({ - id = 'manager_options', - title = 'Verwalter: ' .. manager.name, - menu = 'managers_menu', - options = { - { - title = 'Entfernen', - description = 'Verwalter entfernen', - icon = 'fas fa-user-minus', - onSelect = function() - TriggerServerEvent('vending:server:removeManager', coords, manager.citizenid) - Wait(500) - openManagersMenu(coords) -- Refresh the menu - end - } - } - }) - lib.showContext('manager_options') - end - }) - end - else - table.insert(options, { - title = 'Keine Verwalter', - description = 'Es sind keine Verwalter vorhanden', - icon = 'fas fa-info-circle', - disabled = true - }) + -- Double-check if player is owner of this machine before proceeding + QBCore.Functions.TriggerCallback('vending:server:isOwner', function(isOwner) + if not isOwner then + QBCore.Functions.Notify('Nur der Besitzer kann Verwalter verwalten!', 'error') + return end - lib.registerContext({ - id = 'managers_menu', - title = 'Verwalter verwalten', - menu = 'vending_owner_menu', - options = options - }) - - lib.showContext('managers_menu') + -- Get current managers + QBCore.Functions.TriggerCallback('vending:server:getManagers', function(managers) + local options = { + { + title = 'Verwalter hinzufügen', + description = 'Neuen Verwalter hinzufügen', + icon = 'fas fa-user-plus', + onSelect = function() + openAddManagerMenu(coords) + end + } + } + + -- Add existing managers with remove option + if #managers > 0 then + for i = 1, #managers do + local manager = managers[i] + table.insert(options, { + title = manager.name, + description = manager.online and 'Online' or 'Offline', + icon = manager.online and 'fas fa-circle text-success' or 'fas fa-circle text-danger', + onSelect = function() + lib.registerContext({ + id = 'manager_options', + title = 'Verwalter: ' .. manager.name, + menu = 'managers_menu', + options = { + { + title = 'Entfernen', + description = 'Verwalter entfernen', + icon = 'fas fa-user-minus', + onSelect = function() + TriggerServerEvent('vending:server:removeManager', coords, manager.citizenid) + Wait(500) + openManagersMenu(coords) -- Refresh the menu + end + } + } + }) + lib.showContext('manager_options') + end + }) + end + else + table.insert(options, { + title = 'Keine Verwalter', + description = 'Es sind keine Verwalter vorhanden', + icon = 'fas fa-info-circle', + disabled = true + }) + end + + lib.registerContext({ + id = 'managers_menu', + title = 'Verwalter verwalten', + menu = 'vending_owner_menu', + options = options + }) + + lib.showContext('managers_menu') + end, coords) end, coords) end -- Open add manager menu function openAddManagerMenu(coords) - QBCore.Functions.TriggerCallback('vending:server:getOnlinePlayers', function(players) - if #players == 0 then - QBCore.Functions.Notify('Keine Spieler online!', 'error') + -- Double-check if player is owner of this machine before proceeding + QBCore.Functions.TriggerCallback('vending:server:isOwner', function(isOwner) + if not isOwner then + QBCore.Functions.Notify('Nur der Besitzer kann Verwalter hinzufügen!', 'error') return end - local options = {} - - for i = 1, #players do - local player = players[i] - table.insert(options, { - title = player.name, - description = 'ID: ' .. player.id, - icon = 'fas fa-user', - onSelect = function() - TriggerServerEvent('vending:server:addManager', coords, player.id) - Wait(500) - openManagersMenu(coords) -- Refresh the menu - end + QBCore.Functions.TriggerCallback('vending:server:getOnlinePlayers', function(players) + if #players == 0 then + QBCore.Functions.Notify('Keine Spieler online!', 'error') + return + end + + local options = {} + + for i = 1, #players do + local player = players[i] + table.insert(options, { + title = player.name, + description = 'ID: ' .. player.id, + icon = 'fas fa-user', + onSelect = function() + TriggerServerEvent('vending:server:addManager', coords, player.id) + Wait(500) + openManagersMenu(coords) -- Refresh the menu + end + }) + end + + lib.registerContext({ + id = 'add_manager_menu', + title = 'Verwalter hinzufügen', + menu = 'managers_menu', + options = options }) - end - - lib.registerContext({ - id = 'add_manager_menu', - title = 'Verwalter hinzufügen', - menu = 'managers_menu', - options = options - }) - - lib.showContext('add_manager_menu') - end) + + lib.showContext('add_manager_menu') + end) + end, coords) end -- Robbery menu @@ -589,27 +644,42 @@ RegisterNetEvent('vending:client:startRobbery', function(data) local entity = data.entity local coords = GetEntityCoords(entity) - lib.registerContext({ - id = 'vending_robbery_confirm', - title = 'Verkaufsautomat aufbrechen', - options = { - { - title = 'Aufbrechen', - description = 'Versuche den Automaten aufzubrechen', - icon = 'fas fa-mask', - onSelect = function() - TriggerServerEvent('vending:server:startRobbery', coords) - end - }, - { - title = 'Abbrechen', - description = 'Aufbruch abbrechen', - icon = 'fas fa-times' - } - } - }) - - lib.showContext('vending_robbery_confirm') + -- Double-check if machine is registered and player cannot manage it + QBCore.Functions.TriggerCallback('vending:server:machineExists', function(exists) + if not exists then + QBCore.Functions.Notify('Dieser Automat ist nicht registriert!', 'error') + return + end + + QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage) + if canManage then + QBCore.Functions.Notify('Du kannst deinen eigenen Automaten nicht aufbrechen!', 'error') + return + end + + lib.registerContext({ + id = 'vending_robbery_confirm', + title = 'Verkaufsautomat aufbrechen', + options = { + { + title = 'Aufbrechen', + description = 'Versuche den Automaten aufzubrechen', + icon = 'fas fa-mask', + onSelect = function() + TriggerServerEvent('vending:server:startRobbery', coords) + end + }, + { + title = 'Abbrechen', + description = 'Aufbruch abbrechen', + icon = 'fas fa-times' + } + } + }) + + lib.showContext('vending_robbery_confirm') + end, coords) + end, coords) end) -- Start robbery animation and progress @@ -677,30 +747,38 @@ end) -- Management menu (alternative opening method) RegisterNetEvent('vending:client:openManagement', function(machine) - lib.registerContext({ - id = 'vending_management', - title = 'Verkaufsautomat #' .. machine.id, - options = { - { - title = 'Inventar öffnen', - description = 'Items hinzufügen oder entfernen', - icon = 'fas fa-box', - onSelect = function() - TriggerServerEvent('vending:server:openStash', machine.coords) - end - }, - { - title = 'Einnahmen: $' .. machine.money, - description = 'Geld abheben', - icon = 'fas fa-money-bill', - onSelect = function() - openWithdrawMenu(machine.coords, machine.money) - end + -- Double-check if player can manage this machine + QBCore.Functions.TriggerCallback('vending:server:canManage', function(canManage) + if not canManage then + QBCore.Functions.Notify('Du hast keine Berechtigung diesen Automaten zu verwalten!', 'error') + return + end + + lib.registerContext({ + id = 'vending_management', + title = 'Verkaufsautomat #' .. machine.id, + options = { + { + title = 'Inventar öffnen', + description = 'Items hinzufügen oder entfernen', + icon = 'fas fa-box', + onSelect = function() + TriggerServerEvent('vending:server:openStash', machine.coords) + end + }, + { + title = 'Einnahmen: $' .. machine.money, + description = 'Geld abheben', + icon = 'fas fa-money-bill', + onSelect = function() + openWithdrawMenu(machine.coords, machine.money) + end + } } - } - }) - - lib.showContext('vending_management') + }) + + lib.showContext('vending_management') + end, machine.coords) end) -- Debug command to check props @@ -760,3 +838,37 @@ RegisterCommand('vendingdebug', function() end end, coords) end, false) + +-- Clear cache command for debugging +RegisterCommand('clearvendingcache', function() + machineData = {} + QBCore.Functions.Notify('Vending machine cache cleared', 'success') +end, false) + +-- Event handler for when player loads +RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() + -- Clear cache when player loads + machineData = {} +end) + +-- Event handler for when player unloads +RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() + -- Clear cache when player unloads + machineData = {} +end) + +-- Event handler for resource start +AddEventHandler('onResourceStart', function(resourceName) + if resourceName == GetCurrentResourceName() then + -- Clear cache when resource starts + machineData = {} + end +end) + +-- Event handler for resource stop +AddEventHandler('onResourceStop', function(resourceName) + if resourceName == GetCurrentResourceName() then + -- Nothing to do here, but good to have for completeness + end +end) +