AI Core
Referencia de la APILib: servidor

Streaming por áreas

AreaStream: objetos del mundo (objetos tirados, plantas, cuerpos, props) enviados solo a los jugadores cercanos, como un snapshot y después solo los cambios.

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

AreaStream

Mantiene los elementos del mundo (objetos tirados, plantas, props colocados, cuerpos) en una cuadrícula de celdas y envía a cada jugador solo los elementos de las celdas que lo rodean. Un jugador que empieza a seguir recibe un snapshot de su área; después solo recibe cambios: un elemento añadido, cambiado, movido o eliminado, o nuevas celdas que entran en alcance cuando se mueve. Quién tiene un elemento se calcula a partir de las celdas, así que no se guarda nada por jugador y elemento, y escala a muchísimos elementos y jugadores. Los seguidores se liberan cuando se desconectan.

Usa un AreaStream por tipo de elemento, creado a nivel de módulo. En el cliente, new AreaFollower(event, cellSize) (Lib: cliente) envía (cx, cy) en su propio evento cada vez que cambia la celda del jugador (se comprueba cada 2 segundos), para followReported; tu código de cliente escucha el event del stream y guarda los elementos.

Lado: Servidor · Espera: no

Sin equivalente en Lua.

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

AreaItem

Lo que debe tener cada elemento:

Prop

Type

AreaStreamOptions

Prop

Type

Elementos

MétodoQué haceDevuelve
set(item: T, resend = true): voidAñade o actualiza un elemento. Los seguidores a su alrededor reciben un put; los seguidores que lo perdieron al moverse reciben un remove. Con resend en false, solo los seguidores que aún no lo tenían reciben el put (para un elemento cuyos clientes siguen sus cambios por sí mismos).void
delete(id): voidElimina un elemento; los seguidores que lo tenían reciben un remove.void
load(items: Iterable<T>): voidRegistra muchos elementos sin un mensaje por cada uno (al cargar desde la base de datos al arrancar) y luego envía de nuevo a cada seguidor su área como snapshot.void
get(id): T | undefinedUn elemento.el elemento o undefined
values(): Generator<T>Todos los elementos.un generador
size: numberCuántos elementos hay.number
cellOf(x, y): { cx, cy }La celda de un punto en la cuadrícula de este stream.coordenadas de celda
countAt(x, y): numberElementos en la celda de un punto (un límite por celda).number
around(x, y, cells = 1): Generator<T>Elementos en la celda de un punto y en cells celdas a su alrededor (comprobaciones de separación y de fusión).un generador
recipients(id): Set<number>Los jugadores cuyo cliente tiene ahora el elemento.un set de ids de servidor
knows(source, id): booleanSi el cliente del jugador tiene ahora el elemento.boolean

Seguidores

MétodoQué haceDevuelve
start(source, at = coordsOf(source)): voidEl jugador empieza a seguir en un punto (por defecto: la posición del ped en el servidor) y recibe un snapshot.void
follow(source, cx, cy): voidLa celda actual del jugador: una primera llamada empieza a seguir con un snapshot, un cambio envía solo la diferencia.void
followReported(source, cx, cy): booleanfollow para una celda que envió el cliente, rechazada salvo que esté a una celda como máximo de la posición del ped en el servidor (la celda de un cliente puede ir una por detrás).true cuando se aceptó
followAll(): voidLee la posición en el servidor de cada seguidor y envía la diferencia a los que cambiaron de celda. Llámalo desde un temporizador every cuando el cliente no informa de las celdas.void
stop(source, tell = false): voidEl jugador deja de seguir (cierre de sesión, un mundo privado). tell vacía su cliente.void
following(source): booleanSi el jugador sigue.boolean

Ejemplo

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 }), // el dueño se queda en el servidor
});

// Al arrancar: plants.load(rowsFromDatabase);

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

// El AreaFollower del cliente informa de su celda cuando cambia.
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);
  },
);

// Los cambios de crecimiento solo llegan a los jugadores cerca de cada planta.
every(60_000, () => {
  for (const plant of plants.values()) if (plant.growth < 100) plants.set({ ...plant, growth: plant.growth + 1 });
});

Nunca construyas tu propio índice de celdas, conjuntos de observadores o código de snapshots junto a él. Los elementos alsoTo de un jugador desconectado se liberan (el siguiente jugador con ese id nunca los recibe), pero tu script sigue actualizando el elemento en sí (dónde está ahora el cuerpo). El cellSize del servidor y el del AreaFollower del cliente deben coincidir.

En esta página