diff --git a/lib/entities/client/client_entity.lua b/lib/entities/client/client_entity.lua index 0b2c8c1..85a3fab 100644 --- a/lib/entities/client/client_entity.lua +++ b/lib/entities/client/client_entity.lua @@ -1,7 +1,6 @@ Utility = Utility or Require("lib/utility/client/utility.lua") Ids = Ids or Require("lib/utility/shared/ids.lua") Point = Point or Require("lib/points/client/points.lua") -ClientEntityActions = ClientEntityActions or Require("lib/entities/client/client_entity_actions_ext.lua") -- Added local Entities = {} -- Stores entity data received from server ClientEntity = {} -- Renamed from BaseEntity @@ -86,8 +85,6 @@ end -- Depricated use ClientEntity.CreateBulk instead ClientEntity.RegisterBulk = ClientEntity.CreateBulk - - --- Unregisters an entity and removes it from the world if spawned. -- @param id string|number The ID of the entity to unregister. function ClientEntity.Destroy(id) @@ -100,7 +97,6 @@ function ClientEntity.Destroy(id) end ClientEntity.Unregister = ClientEntity.Destroy - --- Updates the data for a registered entity. -- @param id string|number The ID of the entity to update. -- @param data table The data fields to update. @@ -165,10 +161,6 @@ function ClientEntity.GetAll() return Entities end -function ClientEntity.RegisterAction(name, func) - ClientEntityActions.RegisterAction(name, func) -end - function ClientEntity.OnCreate(_type, func) ClientEntity.OnCreates = ClientEntity.OnCreates or {} if not ClientEntity.OnCreates[_type] then @@ -203,11 +195,22 @@ function ClientEntity.UpdateCoords(id, coords) end entityData.coords = coords Point.UpdateCoords(id, coords) - if entityData.spawned and DoesEntityExist(entityData.spawned) then - SetEntityCoords(entityData.spawned, coords.x, coords.y, coords.z, false, false, false, true) - end + if not entityData.spawned or not DoesEntityExist(entityData.spawned) then return end + SetEntityCoords(entityData.spawned, coords.x, coords.y, coords.z, false, false, false, true) end +function ClientEntity.ChangeModel(id, model) + local entityData = Entities[id] + if not entityData then return print(string.format("[ClientEntity] ChangeModel: Entity %s does not exist", id)) end + + entityData.model = model + if not entityData.spawned or not DoesEntityExist(entityData.spawned) then return end + + RemoveEntity(entityData) + SpawnEntity(entityData) +end + + -- Network Event Handlers RegisterNetEvent("community_bridge:client:CreateEntity", function(entityData) ClientEntity.Create(entityData) @@ -225,46 +228,6 @@ RegisterNetEvent("community_bridge:client:UpdateEntity", function(id, data) ClientEntity.Update(id, data) end) --- New handler for entity actions -RegisterNetEvent("community_bridge:client:TriggerEntityAction", function(entityId, actionName, ...) - local entityData = Entities[entityId] - -- Check if entity exists locally (it doesn't need to be spawned to queue actions) - if entityData then - if actionName == "Stop" then - ClientEntityActions.StopAction(entityId) - elseif actionName == "Skip" then - ClientEntityActions.SkipAction(entityId) - else - print(string.format("[ClientEntity] Triggering action '%s' for entity %s", actionName, entityId)) - local currentAction = ClientEntityActions.ActionQueue[entityId] and ClientEntityActions.ActionQueue[entityId][1] - ClientEntityActions.QueueAction(entityData, actionName, ...) - end - -- else - -- Optional: Log if action received but entity doesn't exist locally at all - -- print(string.format("[ClientEntity] Received action '%s' for non-existent entity %s.", actionName, entityId)) - end -end) - -RegisterNetEvent("community_bridge:client:TriggerEntityActions", function(entityId, actions, endPosition) - local entityData = Entities[entityId] - if entityData then - for _, actionData in pairs(actions) do - local actionName = actionData.name - local actionParams = actionData.params - if actionName == "Stop" then - ClientEntityActions.StopAction(entityId) - elseif actionName == "Skip" then - ClientEntityActions.SkipAction(entityId) - else - local currentAction = ClientEntityActions.ActionQueue[entityId] and ClientEntityActions.ActionQueue[entityId][1] - ClientEntityActions.QueueAction(entityData, actionName, table.unpack(actionParams)) - end - end - else - print(string.format("[ClientEntity] Received actions for non-existent entity %s.", entityId)) - end -end) - -- Resource Stop Cleanup AddEventHandler('onResourceStop', function(resourceName) if resourceName == GetCurrentResourceName() then diff --git a/lib/entities/client/client_entity_actions.lua b/lib/entities/client/client_entity_actions.lua deleted file mode 100644 index 31b66b3..0000000 --- a/lib/entities/client/client_entity_actions.lua +++ /dev/null @@ -1,136 +0,0 @@ - - -ClientEntityActions = {} -ClientEntityActions.ActionThreads = {} -- Store running action threads { [entityId] = thread } -ClientEntityActions.ActionQueue = {} -- Stores pending actions { [entityId] = {{name="ActionName", args={...}}, ...} } -ClientEntityActions.IsActionRunning = {} -- Tracks if an action is currently running { [entityId] = boolean } -ClientEntityActions.RegisteredActions = {} -- New: Registry for action implementations { [actionName] = function(entityData, ...) } --- Forward declaration - - ---- Processes the next action in the queue for a given entity. --- @param entityId string|number The ID of the entity. -function ClientEntityActions.ProcessNextAction(entityId) - if ClientEntityActions.IsActionRunning [entityId] then return end -- Already running something - local queue = ClientEntityActions.ActionQueue[entityId] - if not queue or #queue == 0 then return end -- Queue is empty - - local nextAction = table.remove(queue, 1) -- Dequeue (FIFO) - local entityData = ClientEntity.Get(entityId) -- Assumes ClientEntity is accessible - -- Check if entity is still valid and spawned before starting next action - if not entityData or not entityData.spawned or not DoesEntityExist(entityData.spawned) then - -- Entity despawned while idle, clear queue and do nothing - ClientEntityActions.ActionQueue[entityId] = nil - return - end - - -- Look up the action in the registry - local actionFunc = ClientEntityActions.RegisteredActions [nextAction.name] - if actionFunc then - -- print(string.format("[ClientEntityActions] Starting action '%s' for entity %s", nextAction.name, entityId)) - ClientEntityActions.IsActionRunning [entityId] = true - -- Call the registered function - ClientEntityActions.ActionQueue[entityId] = actionFunc(entityData, table.unpack(nextAction.args)) - if not ClientEntityActions.ActionQueue[entityId] then - -- If the action function doesn't return a queue, set it to nil - ClientEntityActions.IsActionRunning[entityId] = false - ClientEntityActions.ProcessNextAction(entityId) -- Try next action if this one failed immediately - end - else - print(string.format("[ClientEntityActions] Unknown action '%s' dequeued for entity %s", nextAction.name, entityId)) - -- Skip unknown action and try the next one immediately - ClientEntityActions.ActionQueue[entityId] = ClientEntityActions.ProcessNextAction(entityId) - end -end ---- Registers a custom action implementation. --- The action function should handle its own logic, including threading if needed, --- and MUST call ClientEntityActions.ProcessNextAction(entityData.id) when it completes or fails, --- after setting ClientEntityActions.IsActionRunning [entityData.id] = false. --- @param actionName string The name used to trigger this action. --- @param actionFunc function The function to execute. Signature: function(entityData, ...) -function ClientEntityActions.RegisterAction(actionName, actionFunc) - if ClientEntityActions.RegisteredActions [actionName] then - print(string.format("[ClientEntityActions] WARNING: Overwriting registered action '%s'", actionName)) - end - assert(type(actionName) == "string", "actionName must be a string") - ClientEntityActions.RegisteredActions[actionName] = actionFunc - -- print(string.format("[ClientEntityActions] Registered action: %s", actionName)) -end - ---- Queues an action for an entity. Starts processing if idle. --- @param entityData table The entity data. --- @param actionName string The name of the action. --- @param ... any Arguments for the action. -function ClientEntityActions.QueueAction(entityData, actionName, ...) - local entityId = entityData.id - if not ClientEntityActions.ActionQueue[entityId] then - ClientEntityActions.ActionQueue[entityId] = {} - end - - local actionArgs = {...} - table.insert(ClientEntityActions.ActionQueue[entityId], { name = actionName, args = actionArgs }) - -- print(string.format("[ClientEntityActions] Queued action '%s' for entity %s. Queue size: %d", actionName, entityId, #ClientEntityActions.ActionQueue[entityId])) - - -- If the entity isn't currently doing anything, start processing immediately - if not ClientEntityActions.IsActionRunning [entityId] then - ClientEntityActions.ProcessNextAction(entityId) - end -end ---- Stops the current action and clears the queue for a specific entity. --- @param entityId string|number The ID of the entity. -function ClientEntityActions.StopAction(entityId) - -- print(string.format("[ClientEntityActions] Stopping all actions for entity %s", entityId)) - ClientEntityActions.ActionQueue[entityId] = nil -- Clear the queue - ClientEntityActions.IsActionRunning [entityId] = false -- Mark as not running (this will stop loops in threads) - - -- Stop current task/thread if applicable - if ClientEntityActions.ActionThreads[entityId] then - -- Lua threads stop themselves based on ClientEntityActions.IsActionRunning flag - ClientEntityActions.ActionThreads[entityId] = nil - end - - -- Specific task clearing for peds - local entityData = ClientEntity.Get(entityId) - if entityData and entityData.spawned and DoesEntityExist(entityData.spawned) then - if IsEntityAPed(entityData.spawned) then - ClearPedTasksImmediately(entityData.spawned) -- Use Immediately for forceful stop - end - -- Other entity types might need different stop logic - end -end ---- Skips the current action and starts the next one in the queue, if any. --- @param entityId string|number The ID of the entity. -function ClientEntityActions.SkipAction(entityId) - if not ClientEntityActions.IsActionRunning [entityId] then - -- print(string.format("[ClientEntityActions] SkipAction called for %s, but no action running.", entityId)) - return -- Nothing to skip - end - -- print(string.format("[ClientEntityActions] Skipping current action for entity %s", entityId)) - - ClientEntityActions.IsActionRunning [entityId] = false -- Mark as not running (this will stop loops in threads) - - -- Stop current task/thread if applicable - if ClientEntityActions.ActionThreads[entityId] then - ClientEntityActions.ActionThreads[entityId] = nil - end - - local entityData = ClientEntity.Get(entityId) - if entityData and entityData.spawned and DoesEntityExist(entityData.spawned) then - if IsEntityAPed(entityData.spawned) then - ClearPedTasksImmediately(entityData.spawned) - end - end - - -- Immediately try to process the next action - ClientEntityActions.ProcessNextAction(entityId) -end --- Add server-callable functions for Stop and Skip -function ClientEntityActions.Stop(entityData) - ClientEntityActions.StopAction(entityData.id) -end - -function ClientEntityActions.Skip(entityData) - ClientEntityActions.SkipAction(entityData.id) -end - -return ClientEntityActions diff --git a/lib/entities/server/server_entity.lua b/lib/entities/server/server_entity.lua index e6784a8..9d00fc4 100644 --- a/lib/entities/server/server_entity.lua +++ b/lib/entities/server/server_entity.lua @@ -29,7 +29,6 @@ end function ServerEntity.Create(id, entityType, model, coords, rotation, meta) local self = ServerEntity.New(id, entityType, model, coords, rotation, meta) if not self then - -- print("Failed to create entity with ID: " .. tostring(id)) return nil end TriggerClientEvent("community_bridge:client:CreateEntity", -1, self) diff --git a/lib/init.lua b/lib/init.lua index 23f448f..b97a442 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -76,7 +76,6 @@ cLib.Point = Point or Require("lib/points/client/pointsv2.lua") cLib.Particle = Particle or Require("lib/particles/client/particles.lua") cLib.Cache = Cache or Require("lib/cache/client/cache.lua") cLib.ClientEntity = ClientEntity or Require("lib/entities/client/client_entity.lua") -cLib.ClientEntityActions = ClientEntityActions or Require("lib/entities/client/client_entity_actions.lua") cLib.ClientStateBag = ClientStateBag or Require("lib/statebags/client/client.lua") cLib.Marker = Marker or Require("lib/markers/client/markers.lua") cLib.Anim = Anim or Require("lib/anim/client/client.lua") diff --git a/modules/target/_default/client.lua b/modules/target/_default/client.lua index 25a3478..33416e7 100644 --- a/modules/target/_default/client.lua +++ b/modules/target/_default/client.lua @@ -1,9 +1,40 @@ ---@diagnostic disable: duplicate-set-field Target = Target or {} +Ids = Ids or Require("lib/utility/shared/ids.lua") local function warnUser() print("Currently Only Targeting Is Supported By Community Bridge, You Are Using A Resource That Requires The Target Module To Be Used.") end +local InteractIds = {} + +Target.GetCanInteract = function(id) + return InteractIds[id] +end + +Target.CreateCanInteract = function(cb) + if not cb then return end + local id = Ids.CreateUniqueId(InteractIds) + InteractIds[id] = { + id = id, + ableToInteract = -1, + onInteract = cb + } + return id +end + +Target.CanInteract = function(id, ...) + local interactData = InteractIds[id] + if not interactData then return true end + if interactData.ableToInteract == -1 then + local cb = interactData.onInteract + local canInteractStatus = cb(...) + interactData.ableToInteract = canInteractStatus and 1 or 0 + SetTimeout(1000, function() + interactData.ableToInteract = -1 + end) + end + return interactData.ableToInteract > 0 +end Target.FixOptions = function(options) for k, v in pairs(options) do @@ -15,6 +46,13 @@ Target.FixOptions = function(options) return action(entityOrData) end options[k].onSelect = select + local optionsCanInteract = v.canInteract + if optionsCanInteract then + local id = Target.CreateCanInteract(optionsCanInteract) + v.canInteract = function(...) + return Target.CanInteract(id, ...) + end + end end return options end @@ -78,7 +116,6 @@ Target.AddLocalEntity = function(entities, _options) end end - Target.AddModel = function(models, options) warnUser() end diff --git a/modules/target/ox_target/client.lua b/modules/target/ox_target/client.lua index 28551ed..129af5b 100644 --- a/modules/target/ox_target/client.lua +++ b/modules/target/ox_target/client.lua @@ -40,6 +40,13 @@ Target.FixOptions = function(options) end v.groups = v.job or v.groups + local optionsCanInteract = v.canInteract + if optionsCanInteract then + local id = Target.CreateCanInteract(optionsCanInteract) + v.canInteract = function(...) + return Target.CanInteract(id, ...) + end + end -- print(json.encode(v)) end return options diff --git a/modules/target/qb-target/client.lua b/modules/target/qb-target/client.lua index ddc1a6a..46288b2 100644 --- a/modules/target/qb-target/client.lua +++ b/modules/target/qb-target/client.lua @@ -40,6 +40,13 @@ Target.FixOptions = function(options) options[k].action = select options[k].job = v.job or v.groups options[k].jobType = v.jobType + local optionsCanInteract = v.canInteract + if optionsCanInteract then + local id = Target.CreateCanInteract(optionsCanInteract) + v.canInteract = function(...) + return Target.CanInteract(id, ...) + end + end end return options end diff --git a/modules/target/sleepless_interact/client.lua b/modules/target/sleepless_interact/client.lua index 95e6375..c9909da 100644 --- a/modules/target/sleepless_interact/client.lua +++ b/modules/target/sleepless_interact/client.lua @@ -29,6 +29,13 @@ Target.FixOptions = function(options) end options[k].onSelect = select options[k].groups = v.job or v.groups + local optionsCanInteract = v.canInteract + if optionsCanInteract then + local id = Target.CreateCanInteract(optionsCanInteract) + v.canInteract = function(...) + return Target.CanInteract(id, ...) + end + end end return options end