rock-mod
Version:
Rock-Mod is a powerful framework designed for creating and managing mods for Grand Theft Auto (GTA) games.
88 lines (87 loc) • 4.7 kB
TypeScript
import { type CCMPEventsManager } from "../../../net/ccmp/events/CCMPEventsManager";
import { type IPlayersManager } from "../../common/player/IPlayersManager";
import { type IWorldObjectsIterator } from "../../common/worldObject/IWorldObjectsIterator";
import { CCMPPlayer } from "./CCMPPlayer";
/**
* Реализация `IPlayersManager` под CCMP — single source of truth для
* `IPlayer`-инстансов и `rm::playerReady`/`rm::playerConnected`-эмиссий
* с настоящими `CCMPPlayer`-инстансами.
*
* ### Хранилище
*
* `Map<id, CCMPPlayer>`. For CCMP players only, `id` and `remoteId` still
* mean the same server/network id. Non-player CCMP wrappers have a separate
* client-local `id` and nullable server `remoteId`.
* `findByID`/`findByRemoteID`/`findByRemoteId` map to one lookup for players.
*
* ### Жизненный цикл local player'а
*
* `ccmp.players.local` (backed by `op_get_local_player_id`) валиден сразу
* после handshake'а — `PlayerManager::set_local_player_id` срабатывает до
* того, как scripts загружаются. Мы используем его, чтобы синхронно
* выпустить `rm::playerReady` как sticky-event (см. ниже):
*
* 1. **На конструкторе** — fast-path. Если local op возвращает игрока,
* эмитим `rm::playerReady` через `emitInternalSticky` с реальным
* `CCMPPlayer`-инстансом. Sticky-кэш гарантирует, что поздние
* подписчики из async DI-bootstrap'а геймода тоже получат сигнал
* при подписке.
* 2. **На `connectionStateChanged`** — fallback / handler реконнекта.
* Если на конструкторе local ещё не был валиден (handshake race),
* `connected = true` запустит retry fast-path. На `connected = false`
* чистим _localPlayer и sticky-кэш, чтобы при следующем connect'е
* эмитнуть с актуальным remoteId.
*
* `CCMPPlayer`-инстанс важен (а не структурный stub) потому что
* `RockModStateAdapter` в геймоде хранит `rockModPlayer`-ссылку и через
* неё вызывает методы (`setNoCollision`, etc). На структурном stub'е
* без методов это падало бы с `TypeError`.
*
* ### Remote-игроки
*
* Источники:
* - `ccmp.players.all` — prepopulation для уже-существующих на момент
* подключения (заполнен Rust'ом в `ClientScriptingService::init`).
* - `rm::playerConnected`/`rm::playerDisconnected` internal events
* (эмитит `CCMPEventsBridge` из нативных `ccmp.on('playerConnected'/...)`).
*/
export declare class CCMPPlayersManager implements IPlayersManager {
private readonly _players;
private readonly _events;
private _localPlayer;
constructor(events: CCMPEventsManager);
findLocalPlayer(): CCMPPlayer | null;
getLocalPlayer(): CCMPPlayer;
findByRemoteId(remoteId: number): CCMPPlayer | null;
getByRemoteId(remoteId: number): CCMPPlayer;
findByName(name: string): CCMPPlayer | null;
getByName(name: string): CCMPPlayer;
syncWithMpPool(): void;
registerById(id: number): CCMPPlayer;
unregisterById(id: number): CCMPPlayer;
findByID(id: number): CCMPPlayer | null;
getByID(id: number): CCMPPlayer;
findByRemoteID(remoteId: number): CCMPPlayer | null;
getByRemoteID(remoteId: number): CCMPPlayer;
deleteById(id: number): CCMPPlayer;
get iterator(): IWorldObjectsIterator<CCMPPlayer>;
private _registerNativePlayerEvents;
private _syncRemotePlayersFromRuntime;
private _syncLocalPlayerFromRuntime;
/**
* Идемпотентная точка создания/обновления CCMPPlayer. Все источники
* (events, prepopulation, fast-path) проходят сюда.
*/
private _ensurePlayer;
/**
* Пытается синхронно достать local player из CCMP, создать CCMPPlayer
* и эмитнуть rm::playerReady как sticky-event. Идемпотентна: повторный
* вызов после успешной эмиссии — no-op (`_localPlayer !== null`).
*/
private _tryEmitLocalPlayerReady;
private _handleDisconnect;
private readonly _iterator;
private _iterateDimension;
private _iterateRange2D;
private _iterateRange3D;
}