HZ Scripts

Panneau d'administration

HZ-Bridge embarque un panneau d'administration centralise dans lequel chaque module HZ-Script se branche. Au lieu que chaque script fournisse sa propre commande + interface pour la config, tout est regroupe derriere un seul /hzpanel accessible aux admins. Le panneau construit automatiquement un editeur type pour chaque champ de config declare dans le bridge_schema.lua de chaque module.

Disponible depuis HZ-Bridge v1.1.0. Les versions anterieures ne fournissaient que le bridge framework / inventaire.

Ouvrir le panneau

En jeu (chat, console F8 ou raccourci clavier) :

/hzpanel
  • Console seulement (F8 serveur) — /hzpanel depuis la console serveur n'ouvre pas le NUI. Tapez-le depuis le jeu.
  • Necessite la permission ACE configuree sous HZBridge.Panel.AcePermission (defaut admin) — voir Configuration des permissions ci-dessous.
  • Appuyez sur Escape ou cliquez sur le bouton fermer pour quitter. Les sessions sont protegees par token et expirent apres HZBridge.Panel.SessionTokenTtlMs (5 min par defaut) — rouvrez pour rafraichir.
Vous pouvez aussi ouvrir directement sur l'onglet d'un module specifique depuis un autre script :
-- server export — ouvre le panneau pour src et preselectionne le module nomme
exports['HZ-Bridge']:OpenPanel(src, 'HZ-Weather')

Configuration des permissions

Le panneau est protege par le noeud ACE canonique admin de FXServer. Deux lignes dans server.cfg sont requises :

add_principal identifier.license:YOUR_LICENSE group.admin
add_ace group.admin admin allow

Remplacez YOUR_LICENSE par la licence du joueur de votre serveur (visible en tapant /hzpanel une fois — la sortie console de l'echec affiche tous vos identifiants + un bloc pret a copier-coller avec votre licence deja interpolee).

Vous pouvez coller les deux lignes directement dans la console serveur pour corriger immediatement — aucun redemarrage requis. Si vous les mettez dans server.cfg, redemarrez FXServer (ou relancez exec server.cfg) pour les charger.

Piege du boilerplate qb-core : un qb-core fraichement installe livre add_ace group.admin command allow dans server.cfg mais pas add_ace group.admin admin allow. Le menu admin de qb-core utilise sa table DB qbcore_admins plutot que le noeud ACE canonique admin, donc la ligne est absente. Presque chaque ticket de permission HZ-Bridge vient de la — ajoutez la seconde ligne et c'est regle.

Le probe framework se rabat sur les tables admin de qb-core / qbx_core / ESX / ox_core si la verification ACE echoue. Donc un joueur marque comme admin dans votre table qbcore_admins peut ouvrir le panneau meme sans la configuration ACE. Mais ACE est la methode recommandee car elle est agnostique au framework et auditable en un seul endroit.

Si la verification echoue, la console serveur affiche :

  • Le noeud ACE exact verifie
  • Tous vos identifiants joueur
  • Une matrice de probe ACE (quels noeuds sont accordes, lesquels ne le sont pas)
  • Les deux lignes exactes add_principal + add_ace a copier-coller, avec votre licence deja interpolee
Regardez l'erreur rouge dans votre console — la correction est juste la. Disponible depuis v1.1.1.
command.hzpanel n'accorde PAS le panneau. Une erreur courante est d'essayer add_ace identifier.X command.hzpanel allow. Ce noeud n'est pas utilise par HZ-Bridge — la verification porte sur l'AcePermission configure (defaut admin), pas sur le nom de la commande.

Noeud ACE personnalise

Si vous ne voulez pas utiliser le noeud canonique admin (par ex. vous voulez qu'un sous-ensemble specifique du staff ouvre le panneau sans leur donner toutes les autres commandes admin), configurez un noeud personnalise dans config.lua :

HZBridge.Panel.AcePermission = 'hzbridge.panel.admin'

Puis dans server.cfg :

add_principal identifier.license:YOUR_LICENSE group.hzbridge.staff
add_ace group.hzbridge.staff hzbridge.panel.admin allow

Le fallback (quand AcePermission est nil ou '') est 'admin' — aligne sur le defaut de config depuis v1.1.1. Les versions anterieures utilisaient 'HZ-Bridge.admin' comme fallback, ce qui etait un ecart documente et bloquait les clients qui n'avaient que l'ACE canonique admin accorde. Mettez a jour vers v1.1.1 si vous etes concerne.


Ce que vous pouvez modifier

Le panneau decouvre automatiquement chaque ressource qui embarque un fichier bridge_schema.lua a sa racine. A partir de HZ-Bridge v1.1.0, les modules suivants fournissent un schema et s'integrent au panneau :

ModuleCe que vous pouvez ajuster
HZ-BridgeTheme global — accent, surfaces, mode gradient, echelle arrondis
HZ-WeatherZones (editeur + carte), previsions, heure, saisons, evenements, config
HZ-TelevisionChaines, valeurs par defaut, options de performance
HZ-AudioMixerVolumes par defaut par categorie, overrides de profil par joueur
Les scripts tiers peuvent ajouter leur propre onglet simplement en fournissant un bridge_schema.lua — voir la section DSL de schema ci-dessous.

Le DSL de schema

Le bridge_schema.lua de chaque module est un petit DSL Lua qui declare la config que le panneau expose. Le DSL s'execute dans un environnement sandbox (pas de _G, pas de io brut, sous-ensembles restreints de string / table / math) donc un schema buggue ou malveillant ne peut pas casser le serveur.

Exemple minimal

-- resources/your-script/bridge_schema.lua
module('your-script', {
    schemaVersion = 1,
    label = { en = 'Your Script', fr = 'Ton script' },
}, function()
    section('general', {
        label = { en = 'General', fr = 'Général' },
    }, function()
        field('Enabled', 'boolean', {
            default = true,
            label = { en = 'Enabled', fr = 'Activé' },
            description = {
                en = 'Master on/off switch for the script.',
                fr = 'Interrupteur global du script.',
            },
        })

field('PlayerLimit', 'number', {
default = 32, min = 1, max = 256, step = 1,
label = { en = 'Max concurrent players', fr = 'Joueurs simultanés max' },
})
end)
end)

C'est tout — ouvrez /hzpanel, l'onglet "Your Script" apparait avec deux champs types, les changements de valeur sont ecrits sur disque et diffuses a chaque client abonne.

Types de champ

TypeRenduNotes
booleanInterrupteur toggle
numberSaisie numerique avec min / max / stepVariante curseur quand step <= 1 et plage bornee
stringSaisie texte, pattern + maxLength optionnels
enumListe deroulante (choix unique)Choix via choices = { 'a', 'b', 'c' }
enum-multiMulti-selection contrainte (chips toggle)Nouveau en v1.1.0 — stocke en list
colorSelecteur de couleur hexValide en #RRGGBB
keybindCapture de raccourci (touche + modificateurs)
listSaisie de tags (ajout/suppression libre)
listEditeur de table inline avec colonnes itemSchema
vehicle-listGrille de selection de vehiculesRemplie automatiquement depuis GetAllVehicleModels()
weapon-listGrille de selection d'armesCatalogue statique, fourni sous web/src/data/weapons.ts
actionBouton qui declenche un evenement serveurUtilisez pour des declencheurs type "Appliquer preset" / "Reset"
enum-actionListe deroulante d'actions
viewComposant React personnalise pleine largeurRegistre sous web/src/views//

Sections, groupes, onglets

  • section(id, { label, tab, dependsOn }, body) — regroupement visuel dans un onglet de module. tab est l'id du sous-onglet (declare via module(name, { tabs = { ... } })). dependsOn = { field = 'X', equals = true } replie la section quand la condition parente est fausse.
  • group(id, { label }, body) — groupe repliable inline dans une section.
  • field(id, type, opts) — un seul champ. opts supporte default, description, requiresRestart, deprecated, hidden, dependsOn, plus les extras specifiques au type.
  • invariant(sectionId, fn) — validation inter-champs. S'execute a chaque tentative de sauvegarde. Retourne nil (ok) ou une chaine (message d'erreur).
  • migrations({ [N] = function(values) ... end }) — incrementez schemaVersion et fournissez une migration pour reecrire les anciennes valeurs sauvegardees.

Vues personnalisees

Si vous avez besoin d'une interface plus specifique que ce que le renderer de formulaire type peut produire (un editeur de carte, un graphique, un calendrier...), declarez un champ view et enregistrez le composant React :

-- bridge_schema.lua
field('ZonesEditor', 'view', {
    component = 'HZ-Weather:ZoneEditor',
    events = {
        upsertZone = 'hz_weather:admin:upsertZone',
        deleteZone = 'hz_weather:admin:deleteZone',
    },
})
// web/src/views/HZ-Weather/index.tsx
import { registerView } from '../registry'
import { ZoneEditor } from './ZoneEditor'
registerView('HZ-Weather:ZoneEditor', ZoneEditor)

HZ-Weather fournit six vues comme implementations de reference : Map, ZoneEditor, Events, Forecast, SeasonsPanel, TimePanel.


Cascade de theme (Apparence)

L'onglet Apparence dans HZ-Bridge centralise les tokens visuels que chaque NUI HZ partage. Un seul changement retinte HZ-Weather, HZ-AudioMixer, HZ-Television en direct — sans redemarrage. Les nouveaux scripts qui se branchent heritent automatiquement de la cascade tant que leur NUI lit les variables CSS canoniques --hz-*.

Axes que vous controlez

AxeClesEffet
Accentaccent / accentHi / goldTrio signature Vice (rose / rose vif / dore). Presets : Vice, Industrial.
Surfacessurface / subpanel / tintPanneau exterieur / sous-panneau interieur / teinte hover. RGB seulement — l'opacite est decidee par chaque consommateur. Presets : Surfaces Vice, Surfaces Neutres.
Mode gradientgradientMode = 'gradient' \'solid'solid aplatit chaque gradient d'accent en remplissage plat — rendu plus calme / style outil bureautique.
ArrondisradiusNone / Xs / Sm / Md / Lg / XlEchelle de coins a six niveaux. Presets : Sharp (0/2/4/6/8/10), Soft (0/3/6/8/12/16), Round (0/4/8/12/16/20), Industrial (0/0/2/2/4/4).

Comment les modules le consomment

Le payload complet est diffuse a chaque fois que l'admin sauvegarde :

-- server (any resource)
AddEventHandler('HZ-Bridge:theme:updated', function(theme)
    -- theme: { preset, accent, accentHi, gold, surface, subpanel, tint,
    --         gradientMode, radiusNone, radiusXs, radiusSm, ..., radiusXl }
end)

-- client
RegisterNetEvent('HZ-Bridge:theme:updated', function(theme)
SendNUIMessage({ action = 'theme', data = theme })
end)

Dans votre NUI, ecrivez chaque cle comme variable CSS sur :root (par ex. --hz-accent-rgb, --hz-surface-rgb, --hz-radius-md). Les composants referencent les variables via rgb(var(--hz-accent-rgb) / 0.45) / var(--hz-radius-md) donc la cascade est automatique.

Obtenez le theme actuel de maniere synchrone au demarrage :

local theme = exports['HZ-Bridge']:GetTheme()

Ou demandez-le via TriggerServerEvent('HZ-Bridge:theme:request') — le serveur repond avec TriggerClientEvent('HZ-Bridge:theme:updated', src, theme).

Une reference complete copier-coller (forwarder Lua + TypeScript + applier vanilla JS + defauts CSS + binding Tailwind + pattern hide-with-HUD) est fournie dans le skill Claude hz-fivem-ui sous examples/theme-cascade-boilerplate.md.

Securite + audit

  • Schemas sandbox — Le DSL s'execute sans io, os, debug bruts, et avec une bibliotheque standard restreinte. Un bridge_schema.lua buggue peut echouer au chargement (logue) mais ne peut pas crasher le serveur.
  • Ecritures rate-limitees — defaut 10 ecritures par 5s par source. Configurable via HZBridge.Panel.RateLimitWrites / RateLimitWindowMs.
  • Tokens de session — chaque ouverture du panneau emet un token ephemere ; les tokens expires recoivent un nouveau bootstrap a la prochaine requete.
  • Pipeline de validation — chaque sauvegarde passe par la validation de type de champ, les verifications optionnelles pattern / min / max, puis les fonctions invariant au niveau section avant que la valeur n'atterrisse sur disque.
  • Log d'audit — chaque sauvegarde acceptee ecrit une ligne dans le fichier d'audit rotatif (data/audit-YYYY-MM-DD.log). Les fichiers rotatifs plus anciens que HZBridge.Panel.AuditRetentionDays (defaut 90) sont supprimes au demarrage. Passez a 365 si vous avez besoin de retention RGPD.
  • Ecritures atomiques — la config effective par module est ecrite dans data/.json via tmpfile + rename. Les ecritures en attente utilisent data/.pending.json et sont commitees de maniere atomique lors d'une sauvegarde reussie.
  • Verification ACE par source a chaque action — pas seulement a l'ouverture du panneau. Un admin dont les droits sont revoques ne peut pas continuer a editer sur un panneau deja ouvert.

Reference