restructured lib portion to match modules also edited community bridges onplayerunload fire to accept the passed src as it was otherwise nil
328 lines
10 KiB
Lua
328 lines
10 KiB
Lua
local Entities = {}
|
|
local isServer = IsDuplicityVersion()
|
|
local registeredFunctions = {}
|
|
ReboundEntities = {}
|
|
|
|
function ReboundEntities.AddFunction(func)
|
|
assert(func, "Func is nil")
|
|
table.insert(registeredFunctions, func)
|
|
return #registeredFunctions
|
|
end
|
|
|
|
function ReboundEntities.RemoveFunction(id)
|
|
assert(id and registeredFunctions[id], "Invalid ID")
|
|
registeredFunctions[id] = false
|
|
end
|
|
|
|
function FireFunctions(...)
|
|
for _, func in pairs(registeredFunctions) do
|
|
func(...)
|
|
end
|
|
end
|
|
|
|
function ReboundEntities.Register(entityData)
|
|
assert(entityData and entityData.position, "Invalid entity data")
|
|
entityData.id = entityData.id or Ids.CreateUniqueId(Entities)
|
|
entityData.isServer = isServer
|
|
setmetatable(entityData, { __tostring = function() return entityData.id end })
|
|
Entities[entityData.id] = entityData
|
|
FireFunctions(entityData)
|
|
return entityData
|
|
end
|
|
|
|
local function Unregister(id)
|
|
Entities[id] = nil
|
|
end
|
|
|
|
function ReboundEntities.GetSyncData(entityData, key)
|
|
return entityData[key]
|
|
end
|
|
|
|
function ReboundEntities.GetAll()
|
|
return Entities
|
|
end
|
|
|
|
function ReboundEntities.GetById(id)
|
|
return Entities[tostring(id)]
|
|
end
|
|
|
|
function ReboundEntities.GetByModel(model)
|
|
local entities = {}
|
|
for _, entity in pairs(Entities) do
|
|
if entity.model == model then
|
|
table.insert(entities, entity)
|
|
end
|
|
end
|
|
return entities
|
|
end
|
|
|
|
function ReboundEntities.GetClosest(pos)
|
|
local closest, closestDist = nil, 9999
|
|
for _, entity in pairs(Entities) do
|
|
local dist = #(pos - entity.position)
|
|
if dist < closestDist then
|
|
closest, closestDist = entity, dist
|
|
end
|
|
end
|
|
return closest
|
|
end
|
|
|
|
function ReboundEntities.GetWithinRadius(pos, radius)
|
|
local entities = {}
|
|
for _, entity in pairs(Entities) do
|
|
if #(pos - entity.position) < radius then
|
|
table.insert(entities, entity)
|
|
end
|
|
end
|
|
return entities
|
|
end
|
|
|
|
function ReboundEntities.SetOnSyncKeyChange(entityData, cb)
|
|
local data = ReboundEntities.GetById(entityData.id or entityData)
|
|
assert(data, "Entity not found")
|
|
data.onSyncKeyChange = cb
|
|
end
|
|
|
|
exports('ReboundEntities', ReboundEntities)
|
|
|
|
if not isServer then goto client end
|
|
|
|
function ReboundEntities.Create(entityData, src)
|
|
local entity = ReboundEntities.Register(entityData)
|
|
assert(entity and entity.position, "Invalid entity data")
|
|
entity.rotation = entity.rotation or vector3(0, 0, entityData.heading or 0)
|
|
TriggerClientEvent(GetCurrentResourceName() .. ":client:CreateReboundEntity", src or -1, entity)
|
|
return entity
|
|
end
|
|
|
|
|
|
function ReboundEntities.SetCheckRestricted(entityData, cb)
|
|
assert(type(cb) == "function", "Check restricted is not a function")
|
|
entityData.restricted = cb
|
|
end
|
|
|
|
|
|
function ReboundEntities.CheckRestricted(src, entityData)
|
|
return entityData.restricted and entityData.restricted(tonumber(src), entityData) or false
|
|
end
|
|
|
|
function ReboundEntities.Refresh(src)
|
|
TriggerClientEvent(GetCurrentResourceName() .. ":client:CreateReboundEntities", tonumber(src), ReboundEntities.GetAccessibleList(src))
|
|
end
|
|
|
|
function ReboundEntities.Delete(id)
|
|
Unregister(id)
|
|
TriggerClientEvent(GetCurrentResourceName() .. ":client:DeleteReboundEntity", -1, id)
|
|
end
|
|
|
|
function ReboundEntities.DeleteMultiple(entityDatas)
|
|
local ids = {}
|
|
for _, data in pairs(entityDatas) do
|
|
Unregister(data.id)
|
|
table.insert(ids, data.id)
|
|
end
|
|
TriggerClientEvent(GetCurrentResourceName() .. ":client:DeleteReboundEntities", -1, ids)
|
|
end
|
|
|
|
function ReboundEntities.CreateMultiple(entityDatas, src, restricted)
|
|
local _entityDatas = {}
|
|
for _, data in pairs(entityDatas) do
|
|
local entity = ReboundEntities.Register(data)
|
|
assert(entity and entity.position, "Invalid entity data")
|
|
entity.rotation = entity.rotation or vector3(0, 0, 0)
|
|
if restricted then ReboundEntities.SetCheckRestricted(entity, restricted) end
|
|
if not ReboundEntities.CheckRestricted(src, entity) then
|
|
_entityDatas[entity.id] = entity
|
|
end
|
|
end
|
|
TriggerClientEvent(GetCurrentResourceName() .. ":client:CreateReboundEntities", src or -1, _entityDatas)
|
|
return _entityDatas
|
|
end
|
|
|
|
function ReboundEntities.SetSyncData(entityData, key, value)
|
|
entityData[key] = value
|
|
if entityData.onSyncKeyChange then
|
|
entityData.onSyncKeyChange(entityData, key, value)
|
|
end
|
|
TriggerClientEvent(GetCurrentResourceName() .. ":client:SetReboundSyncData", -1, entityData.id, key, value)
|
|
end
|
|
|
|
function ReboundEntities.GetAccessibleList(src)
|
|
local list = {}
|
|
for _, data in pairs(ReboundEntities.GetAll()) do
|
|
if not ReboundEntities.CheckRestricted(src, data) then
|
|
list[data.id] = data
|
|
end
|
|
end
|
|
return list
|
|
end
|
|
|
|
RegisterNetEvent("playerJoining", function()
|
|
ReboundEntities.Refresh(source)
|
|
end)
|
|
|
|
AddEventHandler('onResourceStart', function(resource)
|
|
if resource == GetCurrentResourceName() then
|
|
Wait(1000)
|
|
for _, src in pairs(GetPlayers()) do
|
|
ReboundEntities.Refresh(src)
|
|
end
|
|
end
|
|
end)
|
|
|
|
::client::
|
|
if isServer then return ReboundEntities end
|
|
|
|
function ReboundEntities.LoadModel(model)
|
|
assert(model, "Model is nil")
|
|
model = type(model) == "number" and model or GetHashKey(model)
|
|
RequestModel(model)
|
|
for i = 1, 100 do
|
|
if HasModelLoaded(model) then return model end
|
|
Wait(100)
|
|
end
|
|
error(string.format("Failed to load model %s", model))
|
|
end
|
|
|
|
function ReboundEntities.Spawn(entityData)
|
|
local model = entityData.model
|
|
local position = entityData.position
|
|
assert(position, "Position is nil")
|
|
local rotation = entityData.rotation or vector3(0, 0, 0)
|
|
local entity = model and CreateObject(ReboundEntities.LoadModel(model), position.x, position.y, position.z, false, false, false)
|
|
if entity then SetEntityRotation(entity, rotation.x, rotation.y, rotation.z) end
|
|
if entityData.onSpawn then entity = entityData.onSpawn(entityData, entity) or entity end
|
|
return entity, true
|
|
end
|
|
|
|
function ReboundEntities.SetOnSpawn(id, cb)
|
|
local entity = ReboundEntities.GetById(tostring(id))
|
|
assert(entity, "Entity not found")
|
|
entity.onSpawn = cb
|
|
return true
|
|
end
|
|
|
|
function ReboundEntities.Despawn(entityData)
|
|
local entity = entityData.entity
|
|
if entityData.onDespawn then entityData.onDespawn(entityData, entity) end
|
|
if entity and DoesEntityExist(entity) then DeleteEntity(entity) end
|
|
return nil, nil
|
|
end
|
|
|
|
function ReboundEntities.SetOnDespawn(id, cb)
|
|
local entity = ReboundEntities.GetById(tostring(id))
|
|
assert(entity, "Entity not found")
|
|
entity.onDespawn = cb
|
|
return true
|
|
end
|
|
|
|
local spawnLoopRunning = false
|
|
|
|
function ReboundEntities.SpawnLoop(distanceToCheck, waitTime)
|
|
if spawnLoopRunning then return end
|
|
spawnLoopRunning = true
|
|
distanceToCheck = distanceToCheck or 50
|
|
waitTime = waitTime or 2500
|
|
CreateThread(function()
|
|
while spawnLoopRunning do
|
|
for _, entity in pairs(Entities) do
|
|
local pos = GetEntityCoords(PlayerPedId())
|
|
local position = vector3(entity.position.x, entity.position.y, entity.position.z)
|
|
local dist = #(pos - position)
|
|
if dist <= distanceToCheck and not entity.entity then
|
|
entity.entity, entity.inRange = ReboundEntities.Spawn(entity)
|
|
elseif dist > distanceToCheck and entity.entity then
|
|
entity.entity, entity.inRange = ReboundEntities.Despawn(entity)
|
|
end
|
|
end
|
|
Wait(waitTime)
|
|
end
|
|
end)
|
|
end
|
|
|
|
function ReboundEntities.GetByEntity(entity)
|
|
for _, data in pairs(Entities) do
|
|
if data.entity == entity then return data end
|
|
end
|
|
end
|
|
|
|
function ReboundEntities.CreateClient(entityData)
|
|
local entity = ReboundEntities.Register(entityData)
|
|
if entity then ReboundEntities.SpawnLoop() end
|
|
return entity
|
|
end
|
|
|
|
function ReboundEntities.DeleteClient(id)
|
|
local entityData = ReboundEntities.GetById(tostring(id))
|
|
assert(not entityData.isServer, "Cannot delete server entity from client")
|
|
if entityData.entity then ReboundEntities.Despawn(entityData) end
|
|
Unregister(id)
|
|
return true
|
|
end
|
|
|
|
function ReboundEntities.CreateMultipleClient(entityDatas)
|
|
local _entityDatas = {}
|
|
for _, data in pairs(entityDatas) do
|
|
local entityData = ReboundEntities.CreateClient(data)
|
|
_entityDatas[entityData.id] = entityData
|
|
end
|
|
return _entityDatas
|
|
end
|
|
|
|
function ReboundEntities.DeleteMultipleClient(ids)
|
|
for _, id in pairs(ids) do
|
|
ReboundEntities.DeleteClient(id)
|
|
end
|
|
end
|
|
|
|
local function DeleteFromServer(id)
|
|
local entityData = ReboundEntities.GetById(tostring(id))
|
|
if entityData then
|
|
entityData.isServer = nil
|
|
ReboundEntities.DeleteClient(id)
|
|
end
|
|
end
|
|
|
|
local function DeleteMultipleFromServer(ids)
|
|
for _, id in pairs(ids) do
|
|
DeleteFromServer(id)
|
|
end
|
|
end
|
|
|
|
RegisterNetEvent(GetCurrentResourceName() .. ":client:CreateReboundEntity", function(entityData)
|
|
ReboundEntities.CreateClient(entityData)
|
|
end)
|
|
|
|
RegisterNetEvent(GetCurrentResourceName() .. ":client:DeleteReboundEntity", function(id)
|
|
DeleteFromServer(id)
|
|
end)
|
|
|
|
RegisterNetEvent(GetCurrentResourceName() .. ":client:CreateReboundEntities", function(entityDatas)
|
|
ReboundEntities.CreateMultipleClient(entityDatas)
|
|
end)
|
|
|
|
RegisterNetEvent(GetCurrentResourceName() .. ":client:DeleteReboundEntities", function(ids)
|
|
ReboundEntities.DeleteMultipleClient(ids)
|
|
end)
|
|
|
|
RegisterNetEvent(GetCurrentResourceName() .. ":client:SetReboundSyncData", function(id, key, value)
|
|
local entityData = ReboundEntities.GetById(id)
|
|
if entityData then
|
|
entityData[key] = value
|
|
if entityData.onSyncKeyChange then
|
|
entityData.onSyncKeyChange(entityData, key, value)
|
|
end
|
|
end
|
|
end)
|
|
|
|
AddEventHandler('onResourceStop', function(resource)
|
|
if resource == GetCurrentResourceName() then
|
|
spawnLoopRunning = false
|
|
for _, entity in pairs(Entities) do
|
|
if entity.entity then DeleteEntity(tonumber(entity.entity)) end
|
|
end
|
|
end
|
|
end)
|
|
|
|
return ReboundEntities
|