Utiliser le coupon WELCOME pour économiser 20 %

$ USD
  • $ USD
  • € EUR
  • £ GBP
  • $ AUD
  • R$ BRL
  • CHF CHF
  • ¥ JPY
Créer une application mobile FiveM personnalisée

Créer une petite application de téléphone FiveM avec NUI

Commencez par créer une petite application NUI de type téléphone pour apprendre l'intégralité du cycle de vie : ouverture, message, callback et fermeture. Cet exemple affiche une note de session qui persiste pendant l'exécution de la ressource. Il n'a ni appels, ni service de messagerie, ni base de données, ni dépendance à un framework ; les ajouter nécessite une autorisation côté serveur et une persistance distinctes.

Créez trois fichiers

Créez resources/[local]/tutorial_phone/ avec fxmanifest.lua, client.lua et index.html. Cet exemple cible l’environnement d’exécution des ressources gta5 de GTA V Legacy. Consultez les consignes de migration vers Enhanced de Cfx.re avant de l’adapter à un déploiement Enhanced.

fxmanifest.lua:

fx_version 'cerulean'
game 'gta5'
ui_page 'index.html'
files { 'index.html' }
client_script 'client.lua'

client.lua:

local visible = false
local ready = false

local function closePhone()
    visible = false
    SetNuiFocus(false, false)
    SendNUIMessage({ action = 'close' })
end

RegisterCommand('tutorial_phone', function()
    if not ready then
        print('Phone UI is loading; try again shortly.')
        return
    end
    if visible then closePhone(); return end
    visible = true
    SetNuiFocus(true, true)
    SendNUIMessage({ action = 'open' })
end, false)

RegisterNUICallback('ready', function(_, cb)
    ready = true
    cb({ ok = true })
end)

RegisterNUICallback('close', function(_, cb)
    closePhone()
    cb({ ok = true })
end)

AddEventHandler('onClientResourceStop', function(name)
    if name == GetCurrentResourceName() and visible then
        SetNuiFocus(false, false)
    end
end)

index.html:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Session note</title>
<style>
body { margin: 0; background: transparent; font: 18px system-ui; }
#phone { position: fixed; right: 4vw; bottom: 4vh; width: min(320px, 85vw);
  padding: 24px; box-sizing: border-box; color: #fff; background: #18222d;
  border-radius: 20px; }
[hidden] { display: none !important; }
textarea { box-sizing: border-box; width: 100%; min-height: 140px; font: inherit;
  margin: 12px 0; padding: 8px; }
button { font: inherit; padding: 10px 16px; cursor: pointer; }
:focus-visible { outline: 3px solid #67d7ff; outline-offset: 3px; }
</style>
</head>
<body>
<section id="phone" role="dialog" aria-label="Session note" hidden>
  <h1>Session note</h1>
  <label for="note">What will your character do next?</label>
  <textarea id="note" maxlength="500"></textarea>
  <p>This note clears when the resource restarts.</p>
  <button id="close" type="button">Close</button>
  <p id="error" role="status"></p>
</section>
<script>
const phone = document.getElementById('phone');
const note = document.getElementById('note');
const error = document.getElementById('error');
async function call(name) {
  const response = await fetch(`https://${GetParentResourceName()}/${name}`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}'
  });
  if (!response.ok) throw new Error('NUI callback failed');
  return response.json();
}
window.addEventListener('message', ({ data }) => {
  if (data?.action === 'open') {
    phone.hidden = false; error.textContent = ''; note.focus();
  } else if (data?.action === 'close') {
    phone.hidden = true;
  }
});
async function close() {
  try { await call('close'); }
  catch { error.textContent = 'Could not close. Check the F8 console.'; }
}
document.getElementById('close').addEventListener('click', close);
window.addEventListener('keydown', event => {
  if (event.key === 'Escape' && !phone.hidden) { event.preventDefault(); close(); }
});
call('ready').catch(() => console.error('Phone readiness callback failed.'));
</script>
</body>
</html>

Démarrer et vérifier

Ajouter ensure tutorial_phone à la configuration serveur active. Sur votre serveur de test, exécutez refresh et ensure tutorial_phone, connectez-vous, puis exécutez tutorial_phone dans F8. L’application doit s’ouvrir et placer le focus sur son champ de note.

Saisissez une note, fermez avec le bouton puis rouvrez. La note doit être conservée pendant cette session de la ressource. Testez Escape, des ouvertures/fermetures répétées et l’arrêt de la ressource alors qu’elle est ouverte ; les commandes du jeu doivent être libérées. Une reconnexion ou un redémarrage de la ressource crée une nouvelle session d’UI et efface la note.

Inspecter la frontière NUI

Le navigateur appelle le callback de la ressource par son nom de parent réel. Lua répond toujours via cb, et le navigateur gère une requête échouée. Suivez les instructions de débogage NUI de Cfx.re pour inspecter la console et les requêtes de callback sur votre client.

Ajoutez de véritables fonctionnalités de téléphone de manière délibérée

Une ressource de téléphone existante peut exposer son propre SDK d’applications ; utilisez cette intégration prise en charge plutôt que de faire tourner un second téléphone complet. Des contacts persistants nécessitent des vérifications de propriété du personnage côté serveur, des longueurs validées, des requêtes paramétrées et une migration pour le schéma installé.

Les véritables appels nécessitent l’intégration prise en charge de la ressource vocale choisie. N’acceptez jamais un identifiant de personnage arbitraire provenant de l’UI comme preuve d’accès, et n’affirmez pas qu’un formulaire de contacts constitue un système téléphonique complet et sécurisé.

Documentation de référence

Cfx.re — callbacks nui · Cfx.re — manifeste de ressource