rock-mod
Version:
Rock-Mod is a powerful framework designed for creating and managing mods for Grand Theft Auto (GTA) games.
75 lines (74 loc) • 3.18 kB
JavaScript
import { ClientInternalEventName } from "../../common/events/types";
const TARGET_FPS = 60;
const TICK_INTERVAL_MS = Math.floor(1000 / TARGET_FPS);
/**
* Достаём `setInterval` из global scope не вызывая ReferenceError.
*
* Под bare deno_core просто `setInterval` бросает `ReferenceError: setInterval
* is not defined` ещё до того как мы можем проверить `typeof`. `globalThis`
* безопасен — обращение к несуществующему свойству даёт `undefined`.
*/
function getSetInterval() {
const fn = globalThis.setInterval;
return typeof fn === "function" ? fn : null;
}
function getClearInterval() {
const fn = globalThis.clearInterval;
return typeof fn === "function" ? fn : null;
}
function getNativeRenderEventSource() {
const maybeCcmp = globalThis.ccmp;
return typeof (maybeCcmp === null || maybeCcmp === void 0 ? void 0 : maybeCcmp.on) === "function" ? maybeCcmp : null;
}
/**
* Per-frame событие `rm::render` под CCMP.
*
* У RageMP клиента есть нативный raw-event `render`, на котором завязаны
* декораторы `@Render` и `@Interval` в геймоде. Современный CCMP runtime
* эмитит coalesced native `ccmp.on("render")` из game loop; используем его,
* чтобы render не зависел от JS timer pump и не копился в очереди.
*
* `setInterval` остаётся только fallback'ом для старых runtime.
*/
export class CCMPRenderTicker {
constructor() {
this._emitter = null;
this._handle = null;
this._nativeRenderRegistered = false;
}
start(emitter) {
var _a;
this._emitter = emitter;
const nativeRender = getNativeRenderEventSource();
if (nativeRender) {
if (!this._nativeRenderRegistered) {
this._nativeRenderRegistered = true;
(_a = nativeRender.on) === null || _a === void 0 ? void 0 : _a.call(nativeRender, "render", () => {
var _a;
(_a = this._emitter) === null || _a === void 0 ? void 0 : _a.emitInternal(ClientInternalEventName.Render);
});
}
return;
}
if (this._handle !== null)
return;
const setIntervalFn = getSetInterval();
if (!setIntervalFn) {
console.warn('[CCMPRenderTicker] native `ccmp.on("render")` and `setInterval` are unavailable. ' +
"`rm::render` will not be emitted.");
return;
}
this._handle = setIntervalFn(() => {
var _a;
(_a = this._emitter) === null || _a === void 0 ? void 0 : _a.emitInternal(ClientInternalEventName.Render);
}, TICK_INTERVAL_MS);
}
stop() {
if (this._handle !== null) {
const clearIntervalFn = getClearInterval();
clearIntervalFn === null || clearIntervalFn === void 0 ? void 0 : clearIntervalFn(this._handle);
this._handle = null;
}
this._emitter = null;
}
}