5MCustoms

Eigenes Modul

Ein eigenes Modul ist eine normale FiveM-Ressource, die das SDK nutzt und sich beim Core anmeldet. Du brauchst dafür keinen Zugriff auf fremden Modulcode.

Aufbau

resources/mein_modul/
├── module.config.mjs      # einzige Quelle der Wahrheit
├── src/
│   ├── server/index.ts
│   ├── client/index.ts
│   └── shared/config.ts
├── locales/
│   ├── de.json
│   └── en.json
├── sql/
│   └── 001_init.sql
└── nui/                   # optional, nur mit Oberfläche

module.config.mjs

Diese Datei beschreibt das Modul. Die fxmanifest.lua wird daraus generiert — bearbeite sie nicht von Hand, dein Eingriff wäre beim nächsten Build weg.

export default {
  name: 'mein_modul',
  version: '0.1.0',
  description: 'Was das Modul tut',
  dependencies: ['5mc_core'],
  sql: ['sql/001_init.sql'],
  nui: {
    page: 'dist/nui/index.html',
    files: ['dist/nui/index.html', 'dist/nui/**/*'],
  },
};

Das Feld sql meldet deine Migrationen beim Core an. Sie laufen automatisch beim Start, auch wenn dein Modul vor dem Core hochkommt.

Server-Einstieg

import { onNet, emitClient, log } from '@5mc/sdk';
import { query } from '@5mc/sdk/db';
import { z } from 'zod';

const core = exports['5mc_core'];

onNet(
  'meinmodul:kaufen',
  z.object({ id: z.string().min(1).max(64) }),
  async (source, { id }) => {
    const char = core.getCharacter(source);
    if (!char) return;

    // Entscheidungen gehören auf den Server. Der Client sagt, was er will,
    // nicht was das Ergebnis ist.
    const preis = await preisFuer(id);
    if (!core.removeMoney(source, 'cash', preis)) {
      emitClient('meinmodul:fehler', source, { grund: 'zuwenig' });
      return;
    }

    log.info(`${char.id} kauft ${id}`);
  },
);

Client-Einstieg

import { onClient, emitServer, callback } from '@5mc/sdk';

// Mit Oberfläche: einmal aufrufen, dann kennen NUI-Seiten Marke und Sprache
import { installNuiEnv } from '@5mc/sdk';
installNuiEnv();

onClient('meinmodul:fehler', ({ grund }) => {
  // ...
});

Bauen und starten

node tools/build-resource.mjs resources/mein_modul

Dann in die server.cfg eintragen — nach 5mc_core — und neu starten.

Während der Entwicklung baut ein Watch-Modus bei jeder Änderung neu:

pnpm dev

Der Neustart im Server passiert dabei nicht automatisch. Mit der Dev-Brücke (nur im Entwicklungsbetrieb) geht das von außen:

node tools/serverctl.mjs restart mein_modul

Vier Regeln, die Ärger sparen

  1. Der Server entscheidet. Der Client meldet Absichten. Preise, Bestände und Rechte prüfst du serverseitig, immer.
  2. Jedes Event bekommt ein Schema. Ohne Schema nimmt das SDK es nicht an.
  3. Keine Dauerschleifen. Arbeite mit Events und Zuständen. Ein while (true) mit kurzem Warten frisst Leistung auf jedem Client.
  4. Texte in die Sprachdateien. Auch deine eigenen. Sonst kann dein Modul nicht mit umgestellt werden, wenn der Server auf Englisch läuft.