ZerØ Network

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

Versión: 1.0.1 | Autor: ZerØ Network | Frameworks: QBCore, Qbox, ESX Legacy, Standalone
Consumo Resmon: 0.00 ms | Almacenamiento: Dual-JSON WAL (Sin base de datos SQL)


1. Filosofía de Diseño y Arquitectura

ZerØ Rent ha sido diseñado desde sus cimientos bajo tres pilares fundamentales:

  1. Independencia Total (100% Standalone): No requiere dependencias propietarias externas (sin zero_os, sin MySQL/MariaDB obligatorio). Funciona directamente en cualquier servidor FiveM con OneSync.
  2. Eficiencia con espera adaptativa: El streaming de NPC usa el intervalo configurable SleepFarMs (1500 ms de fábrica). La comprobación por fotograma sólo se activa junto al punto para leer la tecla de interacción o mientras se utiliza el editor 3D. El objetivo medible en reposo es 0.00 ms.
  3. Persistencia Transaccional (Write-Ahead Logging - WAL): Los datos se registran en pares atómicos de archivos JSON (catalog.json y rentals.json con sus respectivos backups). Cualquier cobro bancario genera primero una transacción en el diario contable; solo tras confirmar la persistencia en disco se entrega el vehículo, evitando duplicaciones de dinero o vehículos fantasma ante caídas inesperadas del servidor.

2. Estructura del Proyecto

zero_rent/
├── fxmanifest.lua               # Manifiesto de FiveM (Cerulean, OneSync, Lua 5.4)
├── shared/
│   ├── config.lua               # Configuración global del recurso
│   ├── config_locales.lua       # Textos en español e inglés del servidor
│   └── schema.lua               # Validador de esquemas, tipos y funciones matemáticas
├── bridge/
│   ├── client.lua               # Puente cliente: textUI, targets, notificaciones y bloqueo garajes
│   └── server.lua               # Puente servidor: frameworks (QBCore, Qbox, ESX), llaves y cobros
├── server/
│   ├── store.lua                # Motor de persistencia transaccional JSON (WAL)
│   ├── rentals.lua              # Lógica de alquileres, detección de espacios, estados y ticks
│   ├── version.lua              # Comprobador protegido de versiones ZerØ Network y banner
│   └── server.lua               # Eventos RPC y ciclo de vida del servidor
├── client/
│   ├── editor.lua               # Modo Fantasma 3D: cámara libre, raycast, rotación y previsualización
│   └── client.lua               # Lógica cliente: menús NUI, streaming 0.00ms, HUD y spawneo seguro
├── html/
│   ├── index.html               # Vista web NUI para cliente y panel de administración
│   ├── style.css                # Estilos responsive modernos Glassmorphism y animaciones
│   ├── app.js                   # Controlador JavaScript de la interfaz
│   ├── locales.js               # Diccionario bilingüe interactivo (ES / EN)
│   └── assets/                  # Iconos Lucide locales y logotipo de marca
├── readme/
│   ├── Documentacion_ES.md      # Esta documentación técnica en español
│   ├── Documentation_EN.md      # Documentación técnica en inglés
│   ├── Instrucciones_ES.md      # Manual de usuario e instalación en español
│   └── Instructions_EN.md      # Manual de usuario e instalación en inglés
└── data/                        # Almacén de datos persistente (creado automáticamente)
    ├── catalog.json             # Puntos de alquiler, vehículos y coordenadas
    ├── catalog.backup.json      # Respaldo automático del catálogo
    ├── rentals.json             # Contratos activos, transacciones e historial contable
    └── rentals.backup.json      # Respaldo automático del registro contable

3. Guía Completa de Configuración (shared/config.lua)

Cada sección del archivo shared/config.lua está comentada y categorizada:

3.1. Idioma y Framework

Config.Locale = 'ES' -- Idioma por defecto: 'ES' (Español) o 'EN' (English)
Config.Framework = 'auto' -- 'auto', 'qbcore', 'qbox' o 'esx'
Config.FrameworkResources = {
    qbcore = 'qb-core',
    qbox = 'qbx_core',
    esx = 'es_extended'
}

3.2. Interacción y TextUI

Config.Interaction = {
    Mode = 'E',             -- 'E' (Proximidad), 'qb-target' o 'ox_target'
    Distance = 2.0,         -- Distancia en metros para interactuar con el NPC
    StreamDistance = 90.0,  -- Distancia para cargar el ped local en memoria
    TextUI = 'auto',        -- 'auto', 'zero_notify', 'ox_lib', 'qb-core', 'esx' o 'native'
    TargetIcon = 'fas fa-car',
    OxIcon = 'fa-solid fa-car'
}

3.3. Sistema Modular de Llaves de Vehículos

Define qué sistema gestiona el bloqueo y entrega de llaves al spawnear:

Config.Keys = {
    System = 'auto', -- Opciones disponibles:
                     -- 'auto'             (Detecta automáticamente el script iniciado)
                     -- 'qbx_vehiclekeys'  (Qbox)
                     -- 'qb-vehiclekeys'   (QBCore)
                     -- 'qs-vehiclekeys'   (Quasar)
                     -- 'wasabi_carlock'   (Wasabi)
                     -- 'cd_garage'        (Codesign)
                     -- 'custom'           (Usa tus ganchos abiertos en bridge/server.lua)
                     -- 'none'             (Control nativo por motor/puertas)
}

3.4. Bloqueo Universal de Garajes

Evita que los vehículos de alquiler sean guardados por los jugadores en sus garajes personales:

Config.Garages = {
    System = 'auto', -- 'auto' o 'custom'
    BlockSave = true -- Bloqueo preventivo en cliente y servidor
}

3.5. Sistema Modular de Notificaciones

Config.Notify = {
    System = 'auto', -- 'auto', 'zero_notify', 'ox_lib', 'okokNotify', 'mythic_notify',
                     -- 'wasabi_notify', 'qbox', 'qbcore', 'esx', 'custom'
    Duration = 7000  -- Duración del mensaje en milisegundos
}

3.6. Sistema Modular de Combustible (Config.Fuel)

Garantiza que cualquier vehículo alquilado salga siempre con el tanque lleno (100%) y compatibilidad total con los sistemas de gasolina modernos sin solapamientos:

Config.Fuel = {
    System = 'auto', -- 'auto', 'ox_fuel', 'LegacyFuel', 'cdn-fuel', 'ps-fuel',
                     -- 'rcore_fuel', 'ti_fuel', 'okokGasStation', 'myFuel', 'native', 'custom'
    Level = 100.0,   -- Nivel de combustible garantizado al entregar (100% tanque lleno)
    StateBag = true  -- Sincronizar StateBag (.state.fuel) compatible con HUDs modernos y Qbox
}

En bridge/client.lua y bridge/server.lua se disponen de las funciones Bridge.SetFuel(vehicle, amount) y del gancho abierto Bridge.CustomSetFuel(vehicle, amount) para programadores con sistemas propios o privados.

3.7. Políticas de Alquiler y Tiempos

Config.Rental = {
    Minutes = { 15, 30, 60, 120 },     -- Opciones de tiempo disponibles
    MaxPerCharacter = 3,               -- Máximo de alquileres simultáneos por personaje
    RefundPercent = 20,                -- Porcentaje de reembolso por devolución anticipada (20%)
    DestroyedPenaltyPercent = 30,      -- Penalización económica por destruir el vehículo (30%)
    PostExpiryDeleteMinutes = 5,       -- Minutos de espera tras vencer antes de eliminar el vehículo
    DisconnectGraceMinutes = 20,       -- Minutos de gracia si el jugador se desconecta del servidor
    PlatePrefix = 'ZR',                -- Prefijo de matrícula (ej: ZR000042)
    AutoRepairOnRenew = true,          -- Reparar vehículo automáticamente al renovar
    MaxPayment = 5000000,              -- Límite máximo de seguridad contra exploits
}

3.7. Paleta de Colores (Config.Colors)

Cada color disponible en el selector visual cuenta con su definición RGB y códigos de pintura de GTA V:

Config.Colors = {
    white  = { label = 'Blanco',   r = 255, g = 255, b = 255, primary = 111, secondary = 111, hex = '#FFFFFF' },
    black  = { label = 'Negro',    r = 15,  g = 15,  b = 15,  primary = 0,   secondary = 0,   hex = '#0F0F0F' },
    red    = { label = 'Rojo',     r = 200, g = 20,  b = 20,  primary = 27,  secondary = 27,  hex = '#C81414' },
    blue   = { label = 'Azul',     r = 20,  g = 80,  b = 200, primary = 64,  secondary = 64,  hex = '#1450C8' },
    purple = { label = 'Púrpura',  r = 120, g = 30,  b = 180, primary = 145, secondary = 145, hex = '#781EB4' },
    -- ...
}

4. Arquitectura del Puente Abierto (bridge/)

Para garantizar que cualquier programador pueda conectar sus propios sistemas propietarios sin alterar el núcleo del script, zero_rent dispone de funciones gancho abiertas:

4.1. Conexión de un Sistema de Llaves Personalizado

  1. En shared/config.lua, configura:
    Config.Keys.System = 'custom'
    
  2. Abre bridge/server.lua y añade tus llamadas:
    function Bridge.CustomGiveKeys(source, entity, contract)
        -- EJEMPLO: Dar llaves en tu script personalizado
        exports['mi_script_llaves']:GiveKey(source, contract.plate)
        -- O mediante evento cliente:
        -- TriggerClientEvent('mi_script_llaves:give', source, contract.plate)
    end
    
    function Bridge.CustomRemoveKeys(source, entity, contract)
        -- EJEMPLO: Retirar llaves en tu script personalizado
        exports['mi_script_llaves']:RemoveKey(source, contract.plate)
    end
    

4.2. Conexión de un Garaje Personalizado

Si tu servidor utiliza un garaje que no comprueba StateBags automáticamente:

  1. En tu script de garaje (cliente o servidor), añade la comprobación mediante la exportación pública de zero_rent:
    -- En tu script de garajes (al intentar guardar un vehículo):
    if exports['zero_rent']:IsRentalVehicle(vehiculo) then
        TriggerEvent('chat:addMessage', { args = { '^1ERROR', 'No puedes guardar un coche de alquiler en tu garaje.' } })
        return false
    end
    
  2. O bien, configura Config.Garages.System = 'custom' y ajusta la función en bridge/client.lua:
    function B.CustomCanStoreVehicle(vehicle)
        -- Retorna false si no debe permitirse el guardado
        local s = Entity(vehicle).state
        if s and (s.zeroRent ~= nil or s.isRental == true) then
            return false
        end
        return true
    end
    

4.3. Conexión de Notificaciones Personalizadas

  1. En shared/config.lua, configura:
    Config.Notify.System = 'custom'
    
  2. En bridge/client.lua:
    function B.CustomNotify(text, kind, duration)
        -- EJEMPLO:
        exports['mi_notificacion']:SendAlert({
            type = kind, -- 'info', 'success', 'error', 'warning'
            message = text,
            time = duration
        })
    end
    

5. Detección Inteligente y Secuencial de Espacios (server/rentals.lua)

La función E.FreeSpace(point, category, bucket, rentingSource) garantiza una asignación matemática limpia y sin falsos positivos:

5.1. Radios de Colisión Efectivos

En GTA V, las plazas de estacionamiento tienen entre 2.2 m y 2.5 m de anchura. Anteriormente, un radio esférico 3D de 4.0 m abarcaba hasta 3 plazas contiguas. El algoritmo actual evalúa la distancia en el plano horizontal (2D XY) con tolerancia Z:

  • Automóviles (car): Radio 1.40 m (Tolerancia Z: 3.0 m).
  • Motocicletas y Bicicletas (motorcycle, bicycle): Radio 1.00 m.
  • Embarcaciones (boat): Radio 3.50 m (o el radio configurado en el spawn).
  • Aeronaves (aircraft): Radio 7.50 m (o el radio configurado en el spawn).

5.2. Exclusión de Entidades no Obstáculo

  • NPC del punto: Queda explícitamente excluido para que el encargado nunca bloquee una bahía cercana.
  • Cliente que alquila: El ped del jugador que realiza la orden de alquiler se ignora para evitar que su proximidad impida la salida.
  • Peatones transeúntes: Solo se consideran obstáculo si se encuentran parados en el centro mismo de la plaza ($< 0.85\text{ m}$).

5.3. Orden Secuencial Estricto (1 al N)

Las bahías de salida de cada punto se comprueban secuencialmente según fueron creadas:

  • Si las bahías 1, 2 y 3 están ocupadas, el sistema asigna la bahía 4.
  • En cuanto la bahía 1 vuelve a quedar vacía, el sistema prioriza nuevamente la bahía 1 para el siguiente alquiler.

6. Motor de Spawneo Seguro y Anti-Catapulta

En versiones anteriores de GTA V y OneSync, invocar la nativa SetVehicleOnGroundProperly sobre bicicletas, motocicletas o aeronaves provocaba una división por cero en las físicas del juego, catapultando el vehículo fuera del mapa (a coordenadas Y: -27647).

6.1. Solución Implementada

  • Eliminación total de SetVehicleOnGroundProperly: La creación en servidor mediante CreateVehicleServerSetter ya sitúa el vehículo exactamente sobre el plano del suelo con elevación Z segura según el tipo:
    • Bicicletas y Motos: +0.05 m
    • Automóviles y Botes: +0.10 m
    • Helicópteros: +0.40 m
    • Aviones: +0.60 m
  • Mapeo Estricto de Clases de FiveM:
    CreateVehicleServerSetter(hash, spawnType, x, y, z, h) recibe estrictamente uno de los 5 tipos reconocidos por el motor de red: 'automobile', 'bike', 'boat', 'heli' o 'plane'.
  • Filtro de Teletransporte:
    Antes de ejecutar TaskWarpPedIntoVehicle, el cliente valida que las coordenadas del vehículo sincronizado pertenezcan a los límites del mapa (|x| < 8000, |y| < 10000, z > -50) y se encuentren a menos de 30 metros de la bahía. Si se detecta cualquier anomalía, el vehículo se reposiciona forzosamente antes de subir al jugador.

7. Exportaciones y StateBags Públicos

7.1. Exportaciones de Cliente

-- Comprueba si una entidad es un vehículo de alquiler activo
local esAlquiler = exports['zero_rent']:IsRentalVehicle(vehiculo)

-- Comprueba si el vehículo puede ser guardado en un garaje (devuelve false para vehículos de alquiler)
local sePuedeGuardar = exports['zero_rent']:CanStoreVehicle(vehiculo)

7.2. Exportaciones de Servidor

-- Comprueba por entidad o texto de matrícula
local esAlquiler = exports['zero_rent']:IsRentalVehicle(entityOrPlate)

-- Comprueba específicamente si una matrícula pertenece al registro contable de alquileres
local esMatriculaAlquiler = exports['zero_rent']:IsRentalPlate("ZR000042")

7.3. StateBags en Entidades de Vehículo

Cuando se crea un vehículo de alquiler, el servidor asigna los siguientes StateBags de red:

Entity(vehicle).state.zeroRent = recordId        -- ID del contrato (ej: 'rent_1792089697_150')
Entity(vehicle).state.isRental = true           -- Indicador booleano universal
Entity(vehicle).state.cannotBeStored = true     -- Bloqueador estándar para garajes modernos
Entity(vehicle).state.isRented = true           -- Compatibilidad retroactiva

8. Sistema de Manifiesto de Versiones (server/version.lua)

Siguiendo el estándar de ZerØ Network, la lógica de versiones es exclusiva del servidor y reside en un único script protegido server/version.lua (fuera de config.lua para evitar manipulaciones de clientes o terceros):

  • Comprobación periódica asíncrona: Al iniciar y cada 60 minutos, el script consulta de forma no bloqueante el repositorio (https://raw.githubusercontent.com/Zer0-Network/zero_rent/main/versions). Si existe una nueva versión, alerta en consola al administrador con el enlace oficial de actualización.
  • Formato del manifiesto remoto (GitHub):
    {
      "schema": 1,
      "resource": "zero_rent",
      "version": "1.0.1",
      "download": "https://github.com/Zer0-Network/zero_rent/releases/latest"
    }