rock-mod
Version:
Rock-Mod is a powerful framework designed for creating and managing mods for Grand Theft Auto (GTA) games.
73 lines (72 loc) • 5.64 kB
TypeScript
import { type IEventsManager } from "../../common/events/IEventsManager";
import { type IClientInternalEvents } from "../../common/events/types";
import { type IClientToServerEvents, type IServerToClientEvents } from "../../../../shared/net/common/events/types";
/**
* Реализация `IEventsManager` поверх нативного CCMP клиента.
*
* Важные особенности CCMP, продиктовавшие дизайн:
*
* 1. У `ccmp.on(name, cb)` **нет** парного `ccmp.off`. Поэтому unsubscribe
* реализован через собственный реестр: на каждое имя регистрируется ровно
* один диспетчер в `ccmp`, который проходит по нашему `Set` подписчиков.
* `off` лишь удаляет колбэк из `Set`, диспетчер на стороне CCMP остаётся
* висеть (становится no-op).
*
* 2. `ccmp.emitServer(event, data)` принимает **одно** значение `data`. Наш
* контракт `emitServer(name, ...args)` — variadic. Чтобы не терять
* позиционные аргументы, всегда отправляем `args` массивом, а на приёме
* разворачиваем обратно. Это совпадает с тем, как сервер уже шлёт клиенту
* (`ccmpPlayer.emit(name, args)` в `CCMPEventsManager` на сервере).
*
* 3. Локальной in-process шины событий (аналог `mp.events.call`) у CCMP нет —
* под `emitInternal` подкладываем `CCMPInProcessEmitter`. Однако `onInternal`
* дополнительно подписывает handler ещё и на нативный `ccmp.on(name, ...)`,
* потому что геймод использует `events.onInternal` как канал для UI-ingress
* (см. `rock-mod-event-emitter.adapter.ts:61-66` в геймоде: `registerUI()`
* делегирует в `events.onInternal`). Под RageMP такого дубля не нужно,
* потому что `mp.events.add` естественно ловит и `mp.events.call`, и
* CEF-выпуски `mp.trigger`. Побочный эффект: server/system события с тем
* же именем тоже долетят до internal-handler'а — на практике конфликтов
* нет, потому что namespace'ы геймода различаются (`rm::*` server,
* `api:*` UI, `player:*`/`vehicle:*`/etc. internal).
*
* 4. `IClientEvents` (для `onRaw`/`offRaw`) — RageMP-specific глобальный
* тип. Под CCMP осмысленно регистрировать `ccmp.on('playerConnected', ...)`
* и другие билтины, но строго типизировать их через `IClientEvents` нельзя
* — используем `@ts-expect-error` по аналогии с серверным `CCMPEventsManager`.
*/
export declare class CCMPEventsManager implements IEventsManager {
/** Реестр всех серверных/raw/generic подписчиков. */
private readonly _externalListeners;
/** Имена, для которых уже зарегистрирован диспетчер в нативном `ccmp`. */
private readonly _dispatched;
/** Локальная in-process шина — питает `onInternal`/`emitInternal`. */
private readonly _internalEmitter;
onRaw(events: Partial<IClientEvents>): void;
offRaw<K extends keyof IClientEvents>(eventName: K, listener?: IClientEvents[K]): void;
onInternal(events: Partial<IClientInternalEvents>): void;
offInternal<K extends keyof IClientInternalEvents>(eventName: K, listener?: IClientInternalEvents[K]): void;
emitInternal<K extends keyof IClientInternalEvents>(eventName: K, ...args: Parameters<IClientInternalEvents[K]>): void;
/**
* Sticky-вариант `emitInternal`. Кэширует последнее значение, чтобы поздние
* подписчики (`onInternal`) получили его сразу при подписке. Использовать
* для one-shot lifecycle-событий (`rm::playerReady` и т.п.), где гонка между
* "событие случилось" и "подписчик зарегистрирован" критична.
*
* См. `CCMPInProcessEmitter.emitSticky` для подробностей семантики.
*/
emitInternalSticky<K extends keyof IClientInternalEvents>(eventName: K, ...args: Parameters<IClientInternalEvents[K]>): void;
/**
* Очищает sticky-кэш для конкретного internal-события. Использовать на
* disconnect/reset чтобы новые подписчики не получали устаревший local-
* player-stub после реконнекта.
*/
clearInternalSticky<K extends keyof IClientInternalEvents>(eventName: K): void;
onServer(events: Partial<IServerToClientEvents>): void;
offServer<K extends keyof IServerToClientEvents>(eventName: K, listener?: IServerToClientEvents[K]): void;
emitServer<K extends keyof IClientToServerEvents>(eventName: K, ...args: Parameters<IClientToServerEvents[K]>): void;
register(event: string, listener: (...args: unknown[]) => void): void;
unregister(event: string): void;
private _registerExternal;
private _unregisterExternal;
}