forked from Simnation/Main
red
This commit is contained in:
parent
9178871ecd
commit
640cdd069b
9 changed files with 583 additions and 269 deletions
|
@ -2,8 +2,14 @@ local Entity = Entity
|
|||
|
||||
local DebugRendering = false
|
||||
local DebugInfos = false
|
||||
|
||||
-- Used to prevent that the main loop tries to render an entity that has/his been/being deleted
|
||||
-- (the for each entity itearate over the old entities until next cycle and so will try to render a deleted entity)
|
||||
local DeletedEntities = {}
|
||||
|
||||
local EntitiesLoaded = false
|
||||
local Entities = {}
|
||||
|
||||
--#region Local functions
|
||||
local GetActiveSlices = function()
|
||||
local slices = GetSurroundingSlices(currentSlice)
|
||||
|
@ -76,41 +82,58 @@ local UnrenderLocalEntity = function(uNetId)
|
|||
local entity = UtilityNet.GetEntityFromUNetId(uNetId)
|
||||
|
||||
if DoesEntityExist(entity) then
|
||||
TriggerEvent("Utility:Net:OnUnrender", uNetId, entity, GetEntityModel(entity))
|
||||
local state = Entity(entity).state
|
||||
|
||||
Citizen.SetTimeout(1, function()
|
||||
if not DoesEntityExist(entity) then
|
||||
if DebugInfos then
|
||||
warn("UnrenderLocalEntity: entity with uNetId: "..uNetId.." already unrendered, skipping this call")
|
||||
if not state.preserved then
|
||||
TriggerEvent("Utility:Net:OnUnrender", uNetId, entity, GetEntityModel(entity))
|
||||
end
|
||||
|
||||
if not DoesEntityExist(entity) then
|
||||
if DebugInfos then
|
||||
warn("UnrenderLocalEntity: entity with uNetId: "..uNetId.." already unrendered, skipping this call")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Remove state change handler (currently used only for attaching)
|
||||
if state.changeHandler then
|
||||
UtilityNet.RemoveStateBagChangeHandler(state.changeHandler)
|
||||
state.changeHandler = nil
|
||||
end
|
||||
|
||||
if state.found then
|
||||
if state.door then
|
||||
if DoesEntityExist(state.door) then
|
||||
SetEntityVisible(state.door, true)
|
||||
SetEntityCollision(state.door, true, true)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local state = Entity(entity).state
|
||||
|
||||
-- Remove state change handler (currently used only for attaching)
|
||||
if state.changeHandler then
|
||||
UtilityNet.RemoveStateBagChangeHandler(state.changeHandler)
|
||||
state.changeHandler = nil
|
||||
end
|
||||
|
||||
if state.found then
|
||||
else
|
||||
local model = GetEntityModel(entity)
|
||||
|
||||
-- Show map object
|
||||
RemoveModelHide(GetEntityCoords(entity), 0.1, model)
|
||||
end
|
||||
|
||||
if state.preserved then
|
||||
SetEntityAsNoLongerNeeded(entity)
|
||||
else
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
|
||||
state.rendered = false
|
||||
EntitiesStates[uNetId] = nil
|
||||
TriggerLatentServerEvent("Utility:Net:RemoveStateListener", 5120, uNetId)
|
||||
end)
|
||||
end
|
||||
|
||||
if not state.preserved then
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
|
||||
state.rendered = false
|
||||
EntitiesStates[uNetId] = nil
|
||||
TriggerLatentServerEvent("Utility:Net:RemoveStateListener", 5120, uNetId)
|
||||
|
||||
if state.preserved then
|
||||
TriggerEvent("Utility:Net:OnUnrender", uNetId, entity, GetEntityModel(entity))
|
||||
|
||||
-- Max 5000ms for the entity to be deleted if it was preserved, if it still exists, delete it (this prevents entity leaks)
|
||||
Citizen.SetTimeout(5000, function()
|
||||
if DoesEntityExist(entity) then
|
||||
warn("UnrenderLocalEntity: entity with uNetId: "..uNetId.." was preserved for more than 5 seconds, deleting it now")
|
||||
DeleteEntity(entity)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
LocalEntities[uNetId] = nil
|
||||
|
@ -141,17 +164,44 @@ local RenderLocalEntity = function(uNetId, entityData)
|
|||
local model = entityData.model
|
||||
local options = entityData.options
|
||||
|
||||
if not options.replace then
|
||||
if not IsModelValid(model) then
|
||||
error("RenderLocalEntity: Model "..model.." is not valid, uNetId: "..uNetId)
|
||||
if options.abstract then
|
||||
if options.replace then
|
||||
error("RenderLocalEntity: abstract entities can't have the \"replace\" option, uNetId: "..uNetId.." model: "..model)
|
||||
end
|
||||
|
||||
if not IsModelValid(model) then
|
||||
RegisterArchetypes(function()
|
||||
return {
|
||||
{
|
||||
flags = 139296,
|
||||
bbMin = vector3(-0.1, -0.1, -0.1),
|
||||
bbMax = vector3(0.1, 0.1, 0.1),
|
||||
bsCentre = vector3(0.0, 0.0, 0.0),
|
||||
bsRadius = 1.0,
|
||||
name = model,
|
||||
textureDictionary = '',
|
||||
physicsDictionary = '',
|
||||
assetName = model,
|
||||
assetType = 'ASSET_TYPE_DRAWABLE',
|
||||
lodDist = 999,
|
||||
specialAttribute = 0
|
||||
}
|
||||
}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
if not IsModelValid(model) then
|
||||
error("RenderLocalEntity: Model "..tostring(model).." is not valid, uNetId: "..uNetId)
|
||||
end
|
||||
|
||||
if not options.abstract then
|
||||
local start = GetGameTimer()
|
||||
while not HasModelLoaded(model) do
|
||||
if (GetGameTimer() - start) > 5000 then
|
||||
error("RenderLocalEntity: Model "..model.." failed to load, uNetId: "..uNetId)
|
||||
end
|
||||
|
||||
|
||||
RequestModel(model)
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
@ -188,15 +238,23 @@ local RenderLocalEntity = function(uNetId, entityData)
|
|||
local distance = options.door and 1.5 or 0.1
|
||||
|
||||
if options.door and interior ~= 0 then
|
||||
Entity(obj).state.door = _obj
|
||||
|
||||
-- Doors inside interiors need to be deleted
|
||||
-- If not deleted the game will be recreate them every time the interior is reloaded (player exit and then re-enter)
|
||||
-- And so there will be 2 copies of the same door
|
||||
DeleteEntity(_obj)
|
||||
SetEntityVisible(_obj, false)
|
||||
SetEntityCollision(_obj, false, false)
|
||||
else
|
||||
CreateModelHideExcludingScriptObjects(coords, distance, model)
|
||||
end
|
||||
else
|
||||
obj = CreateObject(model, coords, false, false, options.door)
|
||||
if options.abstract then
|
||||
obj = old_CreateObject(model, coords, false, false, options.door)
|
||||
else
|
||||
obj = CreateObject(model, coords, false, false, options.door)
|
||||
end
|
||||
|
||||
SetEntityCoords(obj, coords) -- This is required to ignore the pivot
|
||||
end
|
||||
|
||||
|
@ -210,6 +268,10 @@ local RenderLocalEntity = function(uNetId, entityData)
|
|||
SetEntityRotation(obj, options.rotation)
|
||||
end
|
||||
|
||||
if options.abstract then
|
||||
Entity(obj).state.abstract_model = model
|
||||
end
|
||||
|
||||
-- Always listen for __attached changes (attach/detach)
|
||||
state.changeHandler = UtilityNet.AddStateBagChangeHandler(uNetId, function(key, value)
|
||||
-- Exit if entity is no longer valid
|
||||
|
@ -226,10 +288,12 @@ local RenderLocalEntity = function(uNetId, entityData)
|
|||
--print("Detach")
|
||||
DetachEntity(obj, true, true)
|
||||
end
|
||||
|
||||
LocalEntities[uNetId].attached = value
|
||||
end
|
||||
end)
|
||||
|
||||
LocalEntities[uNetId] = {obj=obj, slice=entityData.slice}
|
||||
LocalEntities[uNetId] = {obj=obj, slice=entityData.slice, createdBy = entityData.createdBy, attached = state.__attached}
|
||||
|
||||
-- Fetch initial state
|
||||
ServerRequestEntityStates(uNetId)
|
||||
|
@ -260,28 +324,26 @@ local CanEntityBeRendered = function(uNetId, entityData, slices)
|
|||
end
|
||||
|
||||
-- Check if entity is within drawing slices (if provided)
|
||||
if slices and not slices[entityData.slice] then
|
||||
if slices and not table.find(slices, entityData.slice) then
|
||||
return false
|
||||
end
|
||||
|
||||
local state = UtilityNet.State(uNetId)
|
||||
|
||||
if DeletedEntities[uNetId] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Render only if within render distance
|
||||
if not state.__attached then
|
||||
local coords = GetEntityCoords(PlayerPedId())
|
||||
local modelsRenderDistance = GlobalState.ModelsRenderDistance
|
||||
local renderDistance = modelsRenderDistance[entityData.model] or 50.0
|
||||
|
||||
if #(entityData.coords - coords) > renderDistance then
|
||||
return false
|
||||
end
|
||||
-- Skip distance check if entity is rendered and attached (keep them alive)
|
||||
if LocalEntities[uNetId] and LocalEntities[uNetId].attached then
|
||||
return true
|
||||
end
|
||||
|
||||
return true
|
||||
local coords = GetEntityCoords(PlayerPedId())
|
||||
local modelsRenderDistance = GlobalState.ModelsRenderDistance
|
||||
local hashmodel = type(entityData.model) == "number" and entityData.model or GetHashKey(entityData.model)
|
||||
local renderDistance = modelsRenderDistance[hashmodel] or 50.0
|
||||
|
||||
return #(entityData.coords - coords) < renderDistance
|
||||
end
|
||||
--#endregion
|
||||
|
||||
|
@ -315,7 +377,6 @@ StartUtilityNetRenderLoop = function()
|
|||
-- Render/Unrender near slices entities
|
||||
UtilityNet.ForEachEntity(function(v)
|
||||
nEntities = nEntities + 1
|
||||
|
||||
if not LocalEntities[v.id] and CanEntityBeRendered(v.id, v) then
|
||||
local obj = UtilityNet.GetEntityFromUNetId(v.id) or 0
|
||||
local state = Entity(obj).state or {}
|
||||
|
@ -342,10 +403,8 @@ StartUtilityNetRenderLoop = function()
|
|||
-- Unrender entities that are out of slice
|
||||
-- Run only if the slice has changed (so something can be out of the slice and need to be unrendered)
|
||||
if lastSlice ~= currentSlice then
|
||||
local entities = GlobalState.Entities
|
||||
|
||||
for netId, data in pairs(LocalEntities) do
|
||||
local entityData = entities[data.slice][netId]
|
||||
local entityData = Entities[data.slice][netId]
|
||||
|
||||
if not CanEntityBeRendered(netId, entityData) then
|
||||
UnrenderLocalEntity(netId)
|
||||
|
@ -368,8 +427,35 @@ StartUtilityNetRenderLoop = function()
|
|||
end
|
||||
|
||||
RegisterNetEvent("Utility:Net:RefreshModel", function(uNetId, model)
|
||||
local timeout = 3000
|
||||
local start = GetGameTimer()
|
||||
while not LocalEntities[uNetId] and (GetGameTimer() - start < 3000) do
|
||||
local entity, slice = nil
|
||||
|
||||
while not entity or not slice do
|
||||
entity, slice = UtilityNet.InternalFindFromNetId(uNetId)
|
||||
|
||||
if (GetGameTimer() - start) > timeout then
|
||||
error("UtilityNet:RefreshModel: Entity existance check timed out for uNetId "..tostring(uNetId))
|
||||
break
|
||||
end
|
||||
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
if entity and Entities[slice] then
|
||||
Entities[slice][uNetId].model = model
|
||||
else
|
||||
error(
|
||||
"Utility:Net:RefreshModel: Entity not found for uNetId " .. tostring(uNetId) ..
|
||||
" setting model " .. tostring(model) ..
|
||||
" entity: " .. tostring(entity) ..
|
||||
", slice: " .. tostring(slice) ..
|
||||
", doesExist? " .. tostring(UtilityNet.DoesUNetIdExist(uNetId))
|
||||
)
|
||||
end
|
||||
|
||||
start = GetGameTimer()
|
||||
while not LocalEntities[uNetId] and (GetGameTimer() - start < timeout) do
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
|
@ -390,13 +476,6 @@ RegisterNetEvent("Utility:Net:RefreshModel", function(uNetId, model)
|
|||
-- Tamper with the entity model and render again
|
||||
local entityData = UtilityNet.InternalFindFromNetId(uNetId)
|
||||
|
||||
if not entityData then
|
||||
error("RefreshModel: entity with uNetId: "..tostring(uNetId).." cant be found")
|
||||
return
|
||||
end
|
||||
|
||||
entityData.model = model
|
||||
|
||||
SetNetIdBeingBusy(uNetId, false)
|
||||
RenderLocalEntity(uNetId, entityData)
|
||||
|
||||
|
@ -415,68 +494,108 @@ RegisterNetEvent("Utility:Net:RefreshModel", function(uNetId, model)
|
|||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("Utility:Net:RefreshCoords", function(uNetId, coords)
|
||||
RegisterNetEvent("Utility:Net:RefreshCoords", function(uNetId, coords, skipPositionUpdate)
|
||||
local start = GetGameTimer()
|
||||
while not LocalEntities[uNetId] and (GetGameTimer() - start < 3000) do
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
if LocalEntities[uNetId] then
|
||||
while not UtilityNet.IsReady(uNetId) or IsNetIdBusy(uNetId) do
|
||||
Citizen.Wait(100)
|
||||
end
|
||||
local entity, slice = UtilityNet.InternalFindFromNetId(uNetId)
|
||||
|
||||
SetNetIdBeingBusy(uNetId, true)
|
||||
SetEntityCoords(LocalEntities[uNetId].obj, coords)
|
||||
SetNetIdBeingBusy(uNetId, false)
|
||||
end
|
||||
end)
|
||||
if entity and Entities[slice] then
|
||||
local newSlice = GetSliceFromCoords(coords)
|
||||
|
||||
RegisterNetEvent("Utility:Net:RefreshRotation", function(uNetId, rotation)
|
||||
local start = GetGameTimer()
|
||||
while not LocalEntities[uNetId] and (GetGameTimer() - start < 3000) do
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
if LocalEntities[uNetId] then
|
||||
while not UtilityNet.IsReady(uNetId) or IsNetIdBusy(uNetId) do
|
||||
Citizen.Wait(100)
|
||||
end
|
||||
|
||||
SetNetIdBeingBusy(uNetId, true)
|
||||
SetEntityRotation(LocalEntities[uNetId].obj, rotation)
|
||||
SetNetIdBeingBusy(uNetId, false)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("Utility:Net:EntityCreated", function(_callId, uNetId)
|
||||
local attempts = 0
|
||||
|
||||
while not UtilityNet.DoesUNetIdExist(uNetId) do
|
||||
attempts = attempts + 1
|
||||
|
||||
if attempts > 5 then
|
||||
if DebugRendering then
|
||||
error("EntityCreated", uNetId, "id not found after 10 attempts")
|
||||
if newSlice ~= slice then
|
||||
local entity = Entities[slice][uNetId]
|
||||
Entities[slice][uNetId] = nil
|
||||
|
||||
if not Entities[newSlice] then
|
||||
Entities[newSlice] = {}
|
||||
end
|
||||
return
|
||||
|
||||
Entities[newSlice][uNetId] = entity
|
||||
|
||||
slice = newSlice
|
||||
end
|
||||
Citizen.Wait(100)
|
||||
|
||||
Entities[slice][uNetId].coords = coords
|
||||
Entities[slice][uNetId].slice = newSlice
|
||||
end
|
||||
|
||||
if CanEntityBeRendered(uNetId) then
|
||||
if not skipPositionUpdate then
|
||||
while not LocalEntities[uNetId] and (GetGameTimer() - start < 3000) do
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
if LocalEntities[uNetId] then
|
||||
while not UtilityNet.IsReady(uNetId) or IsNetIdBusy(uNetId) do
|
||||
Citizen.Wait(100)
|
||||
end
|
||||
|
||||
SetNetIdBeingBusy(uNetId, true)
|
||||
SetEntityCoords(LocalEntities[uNetId].obj, coords)
|
||||
SetNetIdBeingBusy(uNetId, false)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("Utility:Net:RefreshRotation", function(uNetId, rotation, skipRotationUpdate)
|
||||
local start = GetGameTimer()
|
||||
local entity, slice = UtilityNet.InternalFindFromNetId(uNetId)
|
||||
|
||||
if entity and Entities[slice] then
|
||||
Entities[slice][uNetId].options.rotation = rotation
|
||||
end
|
||||
|
||||
if not skipRotationUpdate then
|
||||
while not LocalEntities[uNetId] and (GetGameTimer() - start < 3000) do
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
if LocalEntities[uNetId] then
|
||||
while not UtilityNet.IsReady(uNetId) or IsNetIdBusy(uNetId) do
|
||||
Citizen.Wait(100)
|
||||
end
|
||||
|
||||
SetNetIdBeingBusy(uNetId, true)
|
||||
SetEntityRotation(LocalEntities[uNetId].obj, rotation)
|
||||
SetNetIdBeingBusy(uNetId, false)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("Utility:Net:EntityCreated", function(_callId, object)
|
||||
local uNetId = object.id
|
||||
local slices = GetActiveSlices()
|
||||
|
||||
if not Entities[object.slice] then
|
||||
Entities[object.slice] = {}
|
||||
end
|
||||
|
||||
Entities[object.slice][object.id] = object
|
||||
|
||||
if CanEntityBeRendered(uNetId, object, slices) then
|
||||
if DebugRendering then
|
||||
print("RenderLocalEntity", uNetId, "EntityCreated")
|
||||
end
|
||||
|
||||
RenderLocalEntity(uNetId)
|
||||
RenderLocalEntity(uNetId, object)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("Utility:Net:RequestDeletion", function(uNetId)
|
||||
local entityData = UtilityNet.InternalFindFromNetId(uNetId)
|
||||
if not entityData then return end
|
||||
|
||||
local slice = GetSliceFromCoords(entityData.coords)
|
||||
|
||||
if LocalEntities[uNetId] then
|
||||
DeletedEntities[uNetId] = true
|
||||
UnrenderLocalEntity(uNetId)
|
||||
|
||||
if Entities[slice] then
|
||||
Entities[slice][uNetId] = nil
|
||||
end
|
||||
else
|
||||
if Entities[slice] then
|
||||
Entities[slice][uNetId] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
@ -487,6 +606,39 @@ Citizen.CreateThread(function()
|
|||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
RegisterNetEvent("Utility:Net:GetEntities", function(entities)
|
||||
Entities = entities
|
||||
EntitiesLoaded = true
|
||||
end)
|
||||
|
||||
TriggerServerEvent("Utility:Net:GetEntities")
|
||||
end)
|
||||
|
||||
AddEventHandler("onResourceStop", function(resource)
|
||||
if resource == GetCurrentResourceName() then
|
||||
for k,v in pairs(LocalEntities) do
|
||||
Citizen.CreateThreadNow(function()
|
||||
DeletedEntities[k] = true
|
||||
UnrenderLocalEntity(k)
|
||||
end)
|
||||
end
|
||||
else
|
||||
for k,v in pairs(LocalEntities) do
|
||||
if v.createdBy == resource then
|
||||
if DebugRendering then
|
||||
print("Unrendering deleted entity", k)
|
||||
end
|
||||
|
||||
Citizen.CreateThreadNow(function()
|
||||
DeletedEntities[k] = true
|
||||
UnrenderLocalEntity(k)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Exports
|
||||
UtilityNet.GetEntityFromUNetId = function(uNetId)
|
||||
return LocalEntities[uNetId]?.obj
|
||||
|
@ -500,6 +652,37 @@ UtilityNet.GetUNetIdFromEntity = function(entity)
|
|||
end
|
||||
end
|
||||
|
||||
UtilityNet.GetuNetIdCreator = function(uNetId)
|
||||
return LocalEntities[uNetId]?.createdBy
|
||||
end
|
||||
|
||||
UtilityNet.GetEntityCreator = function(entity)
|
||||
return UtilityNet.GetuNetIdCreator(UtilityNet.GetUNetIdFromEntity(entity))
|
||||
end
|
||||
|
||||
UtilityNet.InternalFindFromNetId = function(uNetId)
|
||||
for sliceI, slice in pairs(Entities) do
|
||||
if slice[uNetId] then
|
||||
return slice[uNetId], sliceI
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
exports("GetEntityFromUNetId", UtilityNet.GetEntityFromUNetId)
|
||||
exports("GetUNetIdFromEntity", UtilityNet.GetUNetIdFromEntity)
|
||||
exports("GetuNetIdCreator", UtilityNet.GetuNetIdCreator)
|
||||
exports("GetEntityCreator", UtilityNet.GetEntityCreator)
|
||||
|
||||
exports("GetRenderedEntities", function() return LocalEntities end)
|
||||
exports("GetEntities", function(slice)
|
||||
while not EntitiesLoaded do
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
if slice then
|
||||
return Entities[slice] or {}
|
||||
else
|
||||
return Entities
|
||||
end
|
||||
end)
|
||||
exports("InternalFindFromNetId", UtilityNet.InternalFindFromNetId)
|
|
@ -1,6 +1,25 @@
|
|||
EntitiesStates = {}
|
||||
|
||||
local function IsEntityStateLoaded(uNetId)
|
||||
return EntitiesStates[uNetId] ~= -1
|
||||
end
|
||||
|
||||
local function EnsureStateLoaded(uNetId)
|
||||
if not IsEntityStateLoaded(uNetId) then
|
||||
local start = GetGameTimer()
|
||||
while not IsEntityStateLoaded(uNetId) do
|
||||
if GetGameTimer() - start > 5000 then
|
||||
error("WaitUntilStateLoaded: entity "..tostring(uNetId).." state loading timed out")
|
||||
break
|
||||
end
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RegisterNetEvent("Utility:Net:UpdateStateValue", function(uNetId, key, value)
|
||||
EnsureStateLoaded(uNetId)
|
||||
|
||||
if not EntitiesStates[uNetId] then
|
||||
EntitiesStates[uNetId] = {}
|
||||
end
|
||||
|
@ -9,14 +28,49 @@ RegisterNetEvent("Utility:Net:UpdateStateValue", function(uNetId, key, value)
|
|||
end)
|
||||
|
||||
GetEntityStateValue = function(uNetId, key)
|
||||
if not EntitiesStates[uNetId] then
|
||||
return
|
||||
if not UtilityNet.GetEntityFromUNetId(uNetId) then -- If trying to get state of entity that isnt loaded
|
||||
local start = GetGameTimer()
|
||||
|
||||
local entity = nil
|
||||
while not entity do
|
||||
entity = UtilityNet.InternalFindFromNetId(uNetId)
|
||||
if GetGameTimer() - start > 2000 then
|
||||
error("GetEntityStateValue: entity "..tostring(uNetId).." doesnt exist, attempted to retrieve key: "..tostring(key))
|
||||
break
|
||||
end
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
||||
return ServerRequestEntityKey(uNetId, key)
|
||||
else
|
||||
EnsureStateLoaded(uNetId)
|
||||
|
||||
if not EntitiesStates[uNetId] then
|
||||
warn("GetEntityStateValue: entity "..tostring(uNetId).." has no loaded states, attempted to retrieve key: "..tostring(key))
|
||||
return
|
||||
end
|
||||
|
||||
return EntitiesStates[uNetId][key]
|
||||
end
|
||||
|
||||
return EntitiesStates[uNetId][key]
|
||||
end
|
||||
|
||||
ServerRequestEntityKey = function(uNetId, key)
|
||||
local p = promise:new()
|
||||
local event = nil
|
||||
|
||||
event = RegisterNetEvent("Utility:Net:GetStateValue"..uNetId, function(value)
|
||||
RemoveEventHandler(event)
|
||||
p:resolve(value)
|
||||
end)
|
||||
|
||||
TriggerServerEvent("Utility:Net:GetStateValue", uNetId, key)
|
||||
return Citizen.Await(p)
|
||||
end
|
||||
|
||||
ServerRequestEntityStates = function(uNetId)
|
||||
EntitiesStates[uNetId] = -1 -- Set as loading
|
||||
|
||||
local p = promise:new()
|
||||
local event = nil
|
||||
|
||||
|
@ -28,7 +82,7 @@ ServerRequestEntityStates = function(uNetId)
|
|||
TriggerServerEvent("Utility:Net:GetState", uNetId)
|
||||
local states = Citizen.Await(p)
|
||||
|
||||
EntitiesStates[uNetId] = states
|
||||
EntitiesStates[uNetId] = states or {}
|
||||
end
|
||||
|
||||
exports("GetEntityStateValue", GetEntityStateValue)
|
||||
|
|
|
@ -1447,6 +1447,28 @@ end
|
|||
|
||||
return values
|
||||
end
|
||||
|
||||
-- Uses table.clone for fast shallow copying (memcpy) before checking and doing actual deepcopy for nested tables
|
||||
-- Handles circular references via seen table
|
||||
-- Significantly faster (~50%) than doing actual deepcopy for flat or lightly-nested structures
|
||||
---@param orig table
|
||||
---@return table
|
||||
table.deepcopy = function(orig, seen)
|
||||
if type(orig) ~= "table" then return orig end
|
||||
seen = seen or {}
|
||||
if seen[orig] then return seen[orig] end
|
||||
|
||||
local copy = table.clone(orig)
|
||||
seen[orig] = copy
|
||||
|
||||
for k, v in next, orig do
|
||||
if type(v) == "table" then
|
||||
copy[k] = table.deepcopy(v, seen)
|
||||
end
|
||||
end
|
||||
|
||||
return copy
|
||||
end
|
||||
|
||||
math.round = function(number, decimals)
|
||||
local _ = 10 ^ decimals
|
||||
|
@ -2512,7 +2534,8 @@ end
|
|||
NetworkRequestControlOfEntity(trolly)
|
||||
end
|
||||
|
||||
local bagObj = CreateObject("hei_p_m_bag_var22_arm_s", vector3(0.0, 0.0, 0.0), true)
|
||||
local playerCoords = GetEntityCoords(ped)
|
||||
local bagObj = CreateObject("hei_p_m_bag_var22_arm_s", playerCoords + vector3(0.0, 0.0, -6.0), true)
|
||||
SetEntityCollision(bagObj, false, true)
|
||||
|
||||
-- Intro
|
||||
|
@ -2984,13 +3007,26 @@ end
|
|||
--// UtilityNet //
|
||||
local CreatedEntities = {}
|
||||
|
||||
local old_GetEntityArchetypeName = GetEntityArchetypeName
|
||||
GetEntityArchetypeName = function(entity)
|
||||
if not entity or not DoesEntityExist(entity) then
|
||||
return ""
|
||||
end
|
||||
|
||||
local res = old_GetEntityArchetypeName(entity)
|
||||
|
||||
if res == "" then
|
||||
return Entity(entity)?.state?.abstract_model or res
|
||||
else
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
--#region API
|
||||
UtilityNet.ForEachEntity = function(fn, slices)
|
||||
if slices then
|
||||
local entities = GlobalState.Entities
|
||||
|
||||
for i = 1, #slices do
|
||||
local _entities = entities[slices[i]]
|
||||
local _entities = UtilityNet.GetEntities(slices[i])
|
||||
local n = 0
|
||||
|
||||
if _entities then
|
||||
|
@ -3009,7 +3045,7 @@ UtilityNet.ForEachEntity = function(fn, slices)
|
|||
end
|
||||
end
|
||||
else
|
||||
local entities = GlobalState.Entities
|
||||
local entities = UtilityNet.GetEntities()
|
||||
|
||||
if not entities then
|
||||
return
|
||||
|
@ -3035,14 +3071,6 @@ UtilityNet.ForEachEntity = function(fn, slices)
|
|||
end
|
||||
end
|
||||
|
||||
UtilityNet.InternalFindFromNetId = function(uNetId)
|
||||
for sliceI, slice in pairs(GlobalState.Entities) do
|
||||
if slice[uNetId] then
|
||||
return slice[uNetId], sliceI
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
UtilityNet.SetDebug = function(state)
|
||||
UtilityNetDebug = state
|
||||
|
||||
|
@ -3071,7 +3099,9 @@ UtilityNet.SetDebug = function(state)
|
|||
for k,v in pairs(localEntities) do
|
||||
local state = UtilityNet.State(v.netId)
|
||||
|
||||
DrawText3Ds(GetEntityCoords(v.obj), "NetId: "..v.netId, 0.25)
|
||||
if DoesEntityExist(v.obj) then
|
||||
DrawText3Ds(GetEntityCoords(v.obj), "NetId: "..v.netId, 0.25)
|
||||
end
|
||||
end
|
||||
Citizen.Wait(1)
|
||||
end
|
||||
|
@ -3089,16 +3119,16 @@ UtilityNet.CreateEntity = function(model, coords, options)
|
|||
|
||||
-- Set resource name in options
|
||||
options = options or {}
|
||||
options.resource = GetCurrentResourceName()
|
||||
options.createdBy = GetCurrentResourceName()
|
||||
|
||||
local callId = math.random(0, 10000000)
|
||||
local event = nil
|
||||
local entity = promise:new()
|
||||
|
||||
-- Callback
|
||||
event = RegisterNetEvent("Utility:Net:EntityCreated", function(_callId, uNetId)
|
||||
event = RegisterNetEvent("Utility:Net:EntityCreated", function(_callId, object)
|
||||
if _callId == callId then
|
||||
entity:resolve(uNetId)
|
||||
entity:resolve(object.id)
|
||||
RemoveEventHandler(event)
|
||||
end
|
||||
end)
|
||||
|
@ -3207,18 +3237,18 @@ UtilityNet.DetachEntity = function(uNetId)
|
|||
end
|
||||
|
||||
-- Using a latent event to prevent blocking the network channel
|
||||
UtilityNet.SetEntityCoords = function(uNetId, coords)
|
||||
UtilityNet.SetEntityCoords = function(uNetId, coords, skipPositionUpdate)
|
||||
TriggerLatentServerEvent("Utility:Net:SetEntityCoords", 5120, uNetId, coords)
|
||||
|
||||
-- Instantly sync for local obj
|
||||
TriggerEvent("Utility:Net:RefreshCoords", uNetId, coords)
|
||||
TriggerEvent("Utility:Net:RefreshCoords", uNetId, coords, skipPositionUpdate)
|
||||
end
|
||||
|
||||
UtilityNet.SetEntityRotation = function(uNetId, rot)
|
||||
TriggerLatentServerEvent("Utility:Net:SetEntityRotation", 5120, uNetId, rot)
|
||||
UtilityNet.SetEntityRotation = function(uNetId, rot, skipRotationUpdate)
|
||||
TriggerLatentServerEvent("Utility:Net:SetEntityRotation", 5120, uNetId, rot, skipRotationUpdate)
|
||||
|
||||
-- Instantly sync for local obj
|
||||
TriggerEvent("Utility:Net:RefreshRotation", uNetId, rot)
|
||||
TriggerEvent("Utility:Net:RefreshRotation", uNetId, rot, skipRotationUpdate)
|
||||
end
|
||||
|
||||
UtilityNet.SetEntityModel = function(uNetId, model)
|
||||
|
@ -3322,6 +3352,22 @@ end
|
|||
UtilityNet.GetUNetIdFromEntity = function(entity)
|
||||
return exports["utility_lib"]:GetUNetIdFromEntity(entity)
|
||||
end
|
||||
|
||||
UtilityNet.GetuNetIdCreator = function(uNetId)
|
||||
return exports["utility_lib"]:GetuNetIdCreator(uNetId)
|
||||
end
|
||||
|
||||
UtilityNet.GetEntityCreator = function(entity)
|
||||
return exports["utility_lib"]:GetEntityCreator(entity)
|
||||
end
|
||||
|
||||
UtilityNet.InternalFindFromNetId = function(uNetId)
|
||||
return exports["utility_lib"]:InternalFindFromNetId(uNetId)
|
||||
end
|
||||
|
||||
UtilityNet.GetEntities = function(slice)
|
||||
return exports["utility_lib"]:GetEntities(slice)
|
||||
end
|
||||
--#endregion
|
||||
|
||||
--#endregion
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue