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.
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.
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étodo | Qué hace | Devuelve |
|---|---|---|
set(item: T, resend = true): void | Añ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): void | Elimina un elemento; los seguidores que lo tenían reciben un remove. | void |
load(items: Iterable<T>): void | Registra 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 | undefined | Un elemento. | el elemento o undefined |
values(): Generator<T> | Todos los elementos. | un generador |
size: number | Cuá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): number | Elementos 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): boolean | Si el cliente del jugador tiene ahora el elemento. | boolean |
Seguidores
| Método | Qué hace | Devuelve |
|---|---|---|
start(source, at = coordsOf(source)): void | El 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): void | La 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): boolean | follow 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(): void | Lee 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): void | El jugador deja de seguir (cierre de sesión, un mundo privado). tell vacía su cliente. | void |
following(source): boolean | Si 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.
Logs
aiLog y aiLogChannels: envía logs a través de ai-core a la consola, Discord, Fivemanage o FiveM Stats, lo que el dueño active en el panel.
Panel de administración
AdminPanel y DataFile: pon los ajustes de tu script en el panel de administración de ai-core, léelos, reacciona a las ediciones y guarda el contenido del panel en data/*.json.