ZerØ Network

Documentación Técnica para Desarrolladores — ZerØ Target

Versión: 1.0.0 | Autor: ZerØ Network (Core Overextended) | Frameworks: QBCore, ESX Legacy, OX Core, Standalone
Consumo Resmon: 0.00 ms (Reposo) / 0.01 ms (Escaneo Activo) | Motor: Lua 5.4 / Cerulean


1. Arquitectura y Filosofía de Diseño

ZerØ Target ha sido desarrollado para resolver de manera definitiva la fragmentación del ecosistema de FiveM en lo relativo a sistemas de interacción ocular (targeting). Tradicionalmente, los servidores se ven forzados a elegir entre qb-target u ox_target, provocando incompatibilidades cuando se integran recursos de terceros con diferentes dependencias.

El diseño de zero_target se fundamenta en cuatro pilares:

  1. Unificación Transparente: Atrapa dinámicamente las llamadas a exports de ox_target, qb-target y qtarget a nivel de runtime mediante metatablas y manejadores __cfx_export_*.
  2. Preservación de Tipos y Seguridad de Invocación: Normaliza las discrepancias de firma entre APIs (por ejemplo, adaptando los callbacks action(entity) de qb-target para que no reciban tablas de respuesta no deseadas, evitando caídas en nativas de GTA V).
  3. Optimización Adaptativa del Ciclo de Renderizado: El raycast principal de la cámara aplica un retardo dinámico (de 25 ms a 200 ms) cuando la retícula apunta al entorno inerte (asfalto, cielo, muros) y entra en 0 ms exclusivamente al detectar entidades con opciones válidas.
  4. Independencia del Framework (Puente Modular): El archivo framework/bridge.lua aísla toda la lógica de permisos, trabajos, bandas e inventarios, permitiendo que el motor funcione idénticamente en QBCore, ESX o Standalone sin código espagueti.

2. Estructura del Proyecto

zero_target/
├── fxmanifest.lua
├── shared/
│   ├── config.lua
│   └── config_locales.lua
├── framework/
│   └── bridge.lua
├── server/
│   ├── main.lua
│   └── version.lua
├── client/
│   ├── main.lua
│   ├── api.lua
│   ├── utils.lua
│   ├── state.lua
│   ├── defaults.lua
│   ├── debug.lua
│   └── compat/
│       ├── qb-target.lua
│       ├── ox_target.lua
│       └── qtarget.lua
├── ui/
│   ├── index.html
│   ├── style.css
│   ├── js/
│   └── sprite/
│       └── pin.png
└── readme/
    ├── Documentacion_ES.md
    ├── Documentation_EN.md
    ├── Instrucciones_ES.md
    └── Instructions_EN.md

3. Capa de Abstracción de Frameworks (framework/bridge.lua)

El puente detecta automáticamente el entorno del servidor o utiliza el valor forzado en Config.Framework.

Sincronización en Tiempo Real

  • Qbox (qbx_core):
    • Detecta automáticamente el framework nativo qbx_core o mediante Config.Framework = "qbox".
    • Escucha qbx_core:client:playerLoaded, qbx_core:client:setPlayerData, qbx_core:client:onJobUpdate, qbx_core:client:onGangUpdate y qbx_core:client:onPlayerUnload.
    • Sincroniza PlayerData.groups, PlayerData.job, PlayerData.gang y PlayerData.citizenid.
    • Soporta la API nativa de permisos exports.qbx_core:HasGroup(filter).
  • QBCore (qb-core):
    • Escucha QBCore:Player:SetPlayerData, QBCore:Client:OnJobUpdate y QBCore:Client:OnGangUpdate.
    • Escucha QBCore:Client:OnPlayerLoaded para inicializar el estado del personaje tras la selección de multicharacter.
    • Escucha QBCore:Client:OnPlayerUnload para purgar la caché de trabajos e ítems en memoria al desconectarse o cambiar de personaje.
  • ESX Legacy (es_extended):
    • Escucha esx:playerLoaded y esx:onPlayerLogout para el ciclo de vida del personaje.
    • Escucha esx:setJob, esx:setJob2 (bandas/segundo trabajo), esx:setPlayerData, esx:addInventoryItem y esx:removeInventoryItem.
  • Inventarios Soportados:
    • ox_inventory: Búsquedas reactivas directas mediante exports.ox_inventory:Search('count', item) y sincronización por evento ox_inventory:itemCount.
    • qb-inventory: Sincronización instantánea mediante el evento QBCore:Client:OnPlayerUpdated (clave 'items') y fallback directo mediante el export exports['qb-inventory']:HasItem(items, amount).
    • Inventarios por Defecto: Extracción protegida de conteos sobre PlayerData.items o PlayerData.inventory en una caché interna indexada.

Métodos del Puente

-- Verifica si el jugador posee un grupo o trabajo con el grado requerido
Bridge.HasGroup(filter)

-- Verifica si el jugador posee los ítems requeridos (admite modo 'anyItem' o todos)
Bridge.HasItem(filter, hasAny)

4. Motor de Escaneo y Optimización de Resmon

El hilo principal de escaneo se aloja en client/main.lua y opera con la siguiente lógica:

[Inicio de Ciclo]
       │
       ▼
¿Modo Activo? (Continuo = true ó Tecla presionada = true)
       ├── NO ──► Sleep = 200 ms (Resmon 0.00 ms)
       │
       └── SÍ
            │
            ▼
   Raycast desde Cámara (Flag 511)
            │
            ▼
    ¿Impacto dentro de DrawDistance (7.0m)?
            ├── NO ──► Sleep = 100 ms a 150 ms
            │
            └── SÍ
                 │
                 ▼
       Evaluar opciones de entidad y zonas cercanas
                 │
                 ▼
       ¿Existen opciones visibles tras filtrar permisos/distancias?
                 ├── SÍ ──► Sleep = 0 ms (Renderizado inmediato a 60 FPS)
                 └── NO ──► Sleep = 60 ms (Sin opciones: CPU descansa)

Gracias a este esquema de espera adaptativa, el consumo medido en resmon es de:

  • 0.00 ms en reposo (conducir, caminar o modo tecla inactivo).
  • 0.01 ms en modo continuo apuntando al suelo o paredes vacías.
  • 0.03 ms - 0.05 ms únicamente durante la fracción de segundo en que se interactúa activamente con el menú desplegado.

5. Capa de Compatibilidad Universal (client/compat/)

5.1. qb-target.lua

Transforma la estructura de opciones de qb-target al formato estándar de zero_target:

  1. Resolución de la Incompatibilidad de action: En qb-target, la firma del callback de acción es tradicionalmente action(entity). En motores derivados de ox, se envía un objeto compuesto response = { entity = ..., coords = ... }.
    Para evitar que scripts antiguos pasen una tabla a nativas como DeleteEntity(entity) o NetworkGetNetworkIdFromEntity(entity), zero_target envuelve la función:
    local action = v.action
    if action then
        v.onSelect = function(data)
            return action(data and data.entity or data, data)
        end
    end
    
  2. Normalización de Grupos y Bandas: Traduce campos heterogéneos como v.job, v.gang y v.citizenid (admitiendo tanto cadenas de texto como arrays o tablas indexadas por grados) en una tabla única compatible con Bridge.HasGroup.
  3. Aliases Plurales: Registra variantes singulares y plurales de todas las funciones comunes:
    • AddTargetBone / AddTargetBones
    • AddTargetEntity / AddTargetEntities / RemoveTargetEntity / RemoveTargetEntities
    • AddTargetModel / AddTargetModels / RemoveTargetModel / RemoveTargetModels
    • RemoveBoxZone / RemovePolyZone / RemoveCircleZone -> RemoveZone
  4. Manejadores de Red: Captura eventos legacy del tipo AddEventHandler('qb-target:client:AddBoxZone', ...).

5.2. qtarget.lua

Proporciona la interfaz simplificada de qtarget:

  • exports.qtarget:Ped(options)
  • exports.qtarget:Vehicle(options)
  • exports.qtarget:Object(options)
  • exports.qtarget:Player(options)
  • Y sus correspondientes métodos de retirada RemovePed, RemoveVehicle, etc.

6. Referencia de la API Central (client/api.lua)

Cualquier recurso moderno puede interactuar directamente con zero_target (o mediante ox_target):

6.1. Creación de Zonas

-- Box Zone (Zona Cúbica)
local zoneId = exports.zero_target:addBoxZone({
    coords = vec3(x, y, z),
    size = vec3(2.0, 2.0, 2.0),
    rotation = 45.0,
    debug = false,
    options = {
        {
            name = 'mi_opcion',
            icon = 'fas fa-box',
            label = 'Abrir Caja',
            groups = 'police',
            onSelect = function(data)
                print("Caja abierta en", data.coords)
            end
        }
    }
})

-- Poly Zone (Zona Poligonal)
local zoneId = exports.zero_target:addPolyZone({
    points = { vec3(x1, y1, z), vec3(x2, y2, z), vec3(x3, y3, z) },
    thickness = 3.0,
    options = { ... }
})

-- Sphere Zone (Zona Esférica)
local zoneId = exports.zero_target:addSphereZone({
    coords = vec3(x, y, z),
    radius = 2.5,
    options = { ... }
})

-- Eliminar cualquier zona por ID o nombre
exports.zero_target:removeZone(zoneId)

6.2. Modelos, Entidades y Globales

-- Añadir opciones a uno o varios modelos (hash o nombre)
exports.zero_target:addModel({'prop_atm_01', 'prop_atm_02'}, {
    {
        name = 'cajero_usar',
        icon = 'fas fa-credit-card',
        label = 'Utilizar Cajero',
        distance = 1.5,
        onSelect = function(data)
            -- Lógica del cajero
        end
    }
})

-- Opciones Globales por Tipo
exports.zero_target:addGlobalPed(options)
exports.zero_target:addGlobalVehicle(options)
exports.zero_target:addGlobalObject(options)
exports.zero_target:addGlobalPlayer(options)

-- Retirada selectiva por label/name o total
exports.zero_target:removeGlobalVehicle({'nombre_opcion'})
exports.zero_target:removeModel('prop_atm_01', {'cajero_usar'})

7. Módulo Servidor y Control de Versiones

server/main.lua

  • Mantiene sincronizado el estado de entidades con target (ox_target:setEntityHasOptions).
  • Dispone de un hilo de recolección de basura cada 10 segundos para desregistrar entidades que hayan sido eliminadas de la memoria del servidor (ox_target:removeEntity).
  • Sincroniza la apertura y cierre de compuertas/puertas de vehículos entre el cliente solicitante y el dueño de la entidad en red (ox_target:toggleEntityDoor).

server/version.lua

  • Cumple estrictamente con los estándares corporativos de ZerØ Network:
    • Comprueba el manifiesto remoto en GitHub en segundo plano sin interrumpir el arranque del servidor.
    • Valida el esquema semántico del JSON (schema == 1).
    • Muestra un banner informativo limpio con el framework detectado, versión instalada y resmon objetivo (0.00 ms).
    • Gestiona automáticamente la limpieza de temporizadores al detener el recurso (onResourceStop).

8. Modos de Interacción y Selección (Config.InteractionType)

El recurso soporta tres modalidades de confirmación de opciones administradas centralmente por el servidor en shared/config.lua:

Modo Clave Icono en Pantalla Mecánica de Confirmación
Mantener (E) 'hold' Letra E estática con onda radar El usuario mantiene presionada la tecla E durante 800ms mientras el borde radial se completa al 100%.
Al Tacto (E) 'press' Letra E estática con onda radar Una sola pulsación rápida de la tecla E ejecuta la acción inmediatamente sin tiempo de espera.
Clic (Ratón) 'click' Mano con dedo índice (fa-hand-pointer) Se muestra la mano con su onda radar y la acción se confirma al presionar el Clic Izquierdo del ratón (o tecla E).

[!IMPORTANT] El modo de interacción es definido exclusivamente por los programadores en el servidor (Config.InteractionType). En la interfaz de usuario (/targetmenu), los jugadores tienen restringida la personalización únicamente a su estilo visual personal: Color de Acento, Tipografía y Forma del Ojo, las cuales se persisten en KVP local (zero_target_shape, zero_target_color, zero_target_font).