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:
- Independencia Total (100% Standalone): No requiere dependencias propietarias externas (sin
zero_os, sin MySQL/MariaDB obligatorio). Funciona directamente en cualquier servidor FiveM con OneSync. - 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 es0.00 ms. - Persistencia Transaccional (Write-Ahead Logging - WAL): Los datos se registran en pares atómicos de archivos JSON (
catalog.jsonyrentals.jsoncon 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
- En
shared/config.lua, configura:Config.Keys.System = 'custom' - Abre
bridge/server.luay 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:
- 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 - O bien, configura
Config.Garages.System = 'custom'y ajusta la función enbridge/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
- En
shared/config.lua, configura:Config.Notify.System = 'custom' - 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): Radio1.40 m(Tolerancia Z:3.0 m). - Motocicletas y Bicicletas (
motorcycle,bicycle): Radio1.00 m. - Embarcaciones (
boat): Radio3.50 m(o el radio configurado en el spawn). - Aeronaves (
aircraft): Radio7.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 medianteCreateVehicleServerSetterya 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
- Bicicletas y Motos:
- 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 ejecutarTaskWarpPedIntoVehicle, 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" }