AI Core
Référence de l’APILib : serveur

Streaming par zone

AreaStream : des objets du monde (objets au sol, plantes, corps, props) envoyés uniquement aux joueurs proches, en un snapshot puis seulement les changements.

TypeScript
import { AreaStream } from "@aicore-fivem/lib/server";

AreaStream

Range les éléments du monde (objets lâchés au sol, plantes, props posés, corps) sur une grille de cellules et n’envoie à chaque joueur que les éléments des cellules qui l’entourent. Un joueur qui commence à suivre reçoit un snapshot de sa zone ; ensuite il ne reçoit que les changements : un élément ajouté, modifié, déplacé ou retiré, ou de nouvelles cellules qui entrent à portée quand il se déplace. Qui possède un élément se déduit des cellules, donc rien n’est stocké par joueur et par élément, et cela tient la charge avec un très grand nombre d’éléments et de joueurs. Les suiveurs sont libérés quand ils se déconnectent.

Utilisez un AreaStream par type d’élément, créé au niveau du module. Côté client, new AreaFollower(event, cellSize) (Lib : client) envoie (cx, cy) sur son propre événement chaque fois que la cellule du joueur change (vérifiée toutes les 2 secondes), pour followReported ; votre code client écoute l’event du stream et conserve les éléments.

Côté : serveur · Attend : non

Pas d’équivalent Lua.

Signature
new AreaStream<T extends AreaItem, V = T>(options: AreaStreamOptions<T, V>)

AreaItem

Ce que chaque élément doit avoir :

Prop

Type

AreaStreamOptions

Prop

Type

Éléments

MéthodeCe qu’elle faitRetour
set(item: T, resend = true): voidAjoute ou met à jour un élément. Les suiveurs autour reçoivent un put ; ceux qui l’ont perdu parce qu’il a bougé reçoivent un remove. Avec resend à false, seuls les suiveurs qui ne l’avaient pas encore reçoivent le put (pour un élément dont les clients suivent eux-mêmes les changements).void
delete(id): voidRetire un élément ; les suiveurs qui l’avaient reçoivent un remove.void
load(items: Iterable<T>): voidRange de nombreux éléments sans message pour chacun (chargement depuis la base de données au démarrage), puis renvoie à chaque suiveur sa zone sous forme de snapshot.void
get(id): T | undefinedUn élément.l’élément ou undefined
values(): Generator<T>Tous les éléments.un générateur
size: numberLe nombre d’éléments.number
cellOf(x, y): { cx, cy }La cellule d’un point sur la grille de ce stream.coordonnées de cellule
countAt(x, y): numberÉléments dans la cellule d’un point (un plafond par cellule).number
around(x, y, cells = 1): Generator<T>Éléments dans la cellule d’un point et dans cells cellules autour (contrôles d’espacement et de fusion).un générateur
recipients(id): Set<number>Les joueurs dont le client possède l’élément en ce moment.un ensemble d’ids serveur
knows(source, id): booleanSi le client du joueur possède l’élément en ce moment.boolean

Suiveurs

MéthodeCe qu’elle faitRetour
start(source, at = coordsOf(source)): voidLe joueur commence à suivre à partir d’un point (par défaut : la position serveur de son ped) et reçoit un snapshot.void
follow(source, cx, cy): voidLa cellule actuelle du joueur : un premier appel lance le suivi avec un snapshot, un changement n’envoie que la différence.void
followReported(source, cx, cy): booleanfollow pour une cellule envoyée par le client, refusée si elle n’est pas à une cellule au plus de la position serveur du ped (la cellule d’un client peut avoir un temps de retard).true si acceptée
followAll(): voidLit la position serveur de chaque suiveur et envoie la différence à ceux qui ont changé de cellule. Appelez-la depuis un minuteur every quand le client ne signale pas ses cellules.void
stop(source, tell = false): voidLe joueur arrête de suivre (déconnexion, monde privé). tell vide son client.void
following(source): booleanSi le joueur suit.boolean

Exemple

import { AreaStream, onClientEvent, onCharacter, every } from "@aicore-fivem/lib/server";
import { readInt } from "@aicore-fivem/lib/shared";

interface Plant { id: number; x: number; y: number; z: number; growth: number; owner: number }

const plants = new AreaStream<Plant, { id: number; x: number; y: number; z: number; growth: number }>({
  event: "my-plants:stream",
  view: ({ id, x, y, z, growth }) => ({ id, x, y, z, growth }), // le propriétaire reste sur le serveur
});

// Au démarrage : plants.load(rowsFromDatabase);

onCharacter({
  loaded: (source) => plants.start(source),
  unloaded: (source) => plants.stop(source),
});

// L'AreaFollower du client signale sa cellule quand elle change.
onClientEvent(
  "my-plants:cell",
  {
    args: ([cx, cy]) => {
      const x = readInt(cx, -1000, 1000);
      const y = readInt(cy, -1000, 1000);
      return x !== null && y !== null ? [x, y] : null;
    },
  },
  (source, cx, cy) => {
    plants.followReported(source, cx, cy);
  },
);

// Les changements de croissance n'atteignent que les joueurs proches de chaque plante.
every(60_000, () => {
  for (const plant of plants.values()) if (plant.growth < 100) plants.set({ ...plant, growth: plant.growth + 1 });
});

Ne construisez jamais votre propre index de cellules, vos propres ensembles d’observateurs ou votre propre code de snapshot à côté. Les éléments alsoTo d’un joueur déconnecté sont relâchés (le prochain joueur qui obtient cet id ne les reçoit jamais), mais votre script continue de mettre à jour l’élément lui-même (l’endroit où le corps se trouve maintenant). Le cellSize du serveur et celui de l’AreaFollower du client doivent correspondre.

Sur cette page