Client JS API
Client-ресурсы используют JavaScript и глобальный объект varion. Runtime инжектит globalThis.varion (доступен также как varion без префикса) до запуска вашего client-main.
Эта страница — справочник по всему runtime-API клиента, сгруппированный по пространствам имён. Смотрите также WebView, GTA natives и краткий обзор клиента.
Терминология: client-events — локальные события внутри клиента; server-events — события, которыми клиент обменивается с сервером. На клиенте нет
varion.onClient— приём событий от сервера делается черезvarion.onServer, аonClientсуществует только на стороне сервера.
Логирование
varion.log('message'); // info
varion.warn('message'); // warning
varion.error('message'); // errorvarion.warn и varion.error — единственные хелперы уровней логирования (logWarning / logError не существуют).
События
Локальные client-события
const sub = varion.on('myEvent', (a, b) => varion.log(`${a} ${b}`));
varion.once('resourceStop', () => varion.log('stopping'));
varion.off('myEvent', handler);
sub.destroy(); // on/once возвращают { destroy() }
varion.emit('myEvent', 1, 2); // локальный эмит для других client-обработчиков
varion.emitRaw('anyEvent', payload); // без фильтра зарезервированных имён (для тестов)varion.emit блокирует зарезервированные движком имена (connection:*, stream:*, playerSpawn, entityStreamIn и т.д.) и имена с префиксом varion:. Такие события слушайте через varion.on, а эмитить их может только движок.
Server-события
varion.onServer('chat:addMessage', (author, text) => {});
varion.onceServer('welcome', (data) => {});
varion.offServer('chat:addMessage', handler);
varion.emitServer('chat:message', 'hello');
varion.emitServerUnreliable('position:update', x, y, z);
// Ненадёжный (unreliable) приём
varion.onServerUnreliable('position:update', (x, y, z) => {});
varion.offServerUnreliable('position:update', handler);
varion.onceServerUnreliable('position:update', handler);Аргументы emitServer автоматически кодируются в типизированные MValue (см. ниже).
Интроспекция обработчиков
varion.getLocalEventListeners('myEvent'); // локальные
varion.getRemoteEventListeners('chat:addMessage'); // серверные (reliable)
varion.getUnreliableRemoteEventListeners('pos'); // серверные (unreliable)Tick-цикл
varion.everyTick(() => { /* ~каждый game tick */ });
varion.on('everyTick', handler); // эквивалентColShape-события (важно про регистр)
Клиент использует именно такой регистр имён:
| Событие | Аргументы |
|---|---|
playerEnterColShape | (colShape) |
playerExitColShape | (colShape) |
entityEnterColshape | (colShape, entity) |
entityLeaveColshape | (colShape, entity) |
Обратите внимание: playerEnter/ExitColShape пишутся с заглавной S, а entityEnter/LeaveColshape — со строчной s (и Leave, а не Exit).
RPC
// Вызов server-RPC и ожидание ответа (Promise)
const result = await varion.callServer('server:ping', arg);
const same = await varion.emitRpc('server:ping', arg); // emitRpc — алиас callServer
// Обработчик client-RPC (сервер вызывает через EmitClientRpcAsync)
varion.onRpc('client:ping', async () => ({ pong: true }));
varion.offRpc('client:ping', handler);callServer / emitRpc возвращают Promise, который резолвится результатом с сервера или реджектится при ошибке/таймауте.
MValue
Фабрика типизированных значений varion.MValue (используется автоматически в emitServer, но доступна и явно):
varion.MValue.nil();
varion.MValue.bool(true);
varion.MValue.int(-5);
varion.MValue.uint(10);
varion.MValue.double(1.5);
varion.MValue.string('hi');
varion.MValue.list(a, b, c);
varion.MValue.dict({ k: 'v' });
varion.MValue.vector3(x, y, z);
varion.MValue.entity(entityType, id);
varion.MValue.rgba(255, 0, 0, 255);
varion.MValue.byteArray(str);
varion.MValue.from(anyValue); // авто-детект типа
varion.MValue.to(mvalue);Хеши и загрузка моделей
const hash = varion.hash('mp_m_freemode_01'); // joaat — алиас hash
varion.joaat('adder');
varion.loadModel('adder');
varion.isModelLoaded('adder'); // → boolean
varion.unloadModel('adder');
await varion.waitForModel('adder', 10000); // Promise, резолвит hash
varion.requestIpl('v_janitor');
varion.removeIpl('v_janitor');Таймеры
Тик-управляемые таймеры (обрабатываются на game tick). Доступны и как методы varion.*, и как глобальные функции.
const t = varion.setTimeout(() => {}, 500);
varion.clearTimeout(t);
const i = varion.setInterval(() => {}, 1000);
varion.clearInterval(i);
setTimeout(fn, 500); // глобальные алиасы тоже доступныТаймеры ресурса автоматически очищаются при остановке ресурса.
Курсор и ввод
varion.isKeyDown(varion.KeyCode.E); // по коду или имени
varion.showCursor(true); // показать/скрыть курсор
varion.getCursorPos(true); // { x, y } (true = нормализованные 0–1)
varion.setCursorPos({ x, y }, true);
varion.toggleGameControls(false); // отключить управление игрой
varion.gameControlsEnabled(); // → booleanvarion.KeyCode(алиасvarion.Key) — карта имён клавиш в коды (A–Z,F1–F12,Numpad0–Numpad9,Shift,Ctrl,Space,MouseLeftи т.д.).varion.GamepadButton— карта кнопок геймпада (A,B,X,Y,LB,RB,DPadUpи т.д.).
Keybind
const kb = new varion.Keybind(varion.KeyCode.E, /* hold */ false);
kb.on('pressed', () => varion.log('E down'));
kb.on('released', () => varion.log('E up'));
kb.gameControlsOnly = true; // не срабатывать при выключенном управлении
kb.enableInUi = false; // не срабатывать при активном фокусе WebView/курсоре
kb.repeatIntervalMs = 0; // >0 — авто-повтор при удержании
kb.isPressed; // текущее состояние
kb.destroy();Сущности
BaseObjectType
varion.BaseObjectType
// { Player:0, Vehicle:1, Ped:2, Object:3, Blip:4, ColShape:7,
// Checkpoint:8, VirtualEntity:9, Marker:22, TextLabel:23 }Базовый Entity
varion.Entity.getByID(type, id);
varion.Entity.getByRemoteID(type, remoteId);
varion.Entity.getByScriptID(scriptId);
varion.Entity.all;Общие геттеры экземпляра: id, remoteId, isRemote, type, scriptID, valid, isStreamedIn, streamed, isSpawned, dimension, isDead, speed, visible, alpha, nativeModel, heading, pos, rot.
Методы экземпляра:
entity.exists();
entity.destroy();
entity.setPos(x, y, z);
entity.setRot(x, y, z);
await entity.setVisible(true);
await entity.setAlpha(200, false);
await entity.resetAlpha();
entity.attachTo(target, bone, offset, rotation, useSoftPinning, collision, fixedRot);
entity.detach();
entity.setCollision(true, true);
await entity.freezePosition(true);
entity.getVariable(key); // synced-мета
entity.hasVariable(key);
await entity.isDeadAsync(); // точное чтение с game-thread
await entity.getSpeedAsync();
await entity.existsAsync();
setPos/setRot/destroyдля чужих игроков server-authoritative и вернутfalse.
Player
const me = varion.Player.local;
varion.Player.get(id);
varion.Player.getByID(id);
varion.Player.getByRemoteID(id);
varion.Player.getByScriptID(scriptId);
varion.Player.all; // включая локального
varion.Player.streamedIn; // только застримленные
varion.Player.count;
varion.getPlayers(); // = Player.allКлючевые геттеры игрока: name (remote), model, health, armour, currentWeapon, currentAmmo, currentWeaponComponents, currentWeaponTintIndex, moveSpeed, forwardSpeed, strafeSpeed, speedVector, aimPos, headRot, entityAimingAt, vehicle, seat, netOwner, isTalking, micLevel, а также флаги состояния isAiming, isJumping, isShooting, isCrouching, isStealthy, isInRagdoll, isReloading, isEnteringVehicle, isLeavingVehicle, isOnLadder, isInMelee, isInCover, isParachuting, flashlightActive.
Player.local дополнительно: weapon, ammoInClip, interiorId, maxHealth, maxArmour, dimension, isDead, isSpawned. Методы: hasWeaponComponent(weapon, component), getWeaponTintIndex(weapon).
Vehicle, Object, Ped
varion.Vehicle.get(id); // .all / .streamedIn / .count / .getByID /
// .getByRemoteID / .getByScriptID
varion.getVehicles();
varion.Object.get(id); // тот же набор статик-методов
varion.getObjects();
varion.Ped.get(id); // тот же набор статик-методов
varion.getPeds();Геттеры Vehicle: model, speedVector, engineOn, engineHealth, petrolTankHealth, gear, rpm, steeringAngle, lockState, isDestroyed, handling, netOwner.
VirtualEntity
varion.VirtualEntity.get(id); // .all / .streamedIn / .getByRemoteID
varion.VirtualEntityGroup.get(id); // .all
varion.VirtualEntityGroup.cellKey(x, y, 100);Выборка по измерению (dimension)
varion.getPlayersInDimension(dim);
varion.getVehiclesInDimension(dim);
varion.getObjectsInDimension(dim);
varion.getPedsInDimension(dim);
varion.getEntitiesInDimension(dim);Метаданные сущности
entity.getMeta(key); entity.setMeta(key, value); // локальная мета
entity.hasMeta(key); entity.deleteMeta(key);
entity.getSyncedMeta(key); entity.hasSyncedMeta(key); // синхронизированная сервером
entity.getStreamSyncedMeta(key); // stream-synced
entity.getMetaDataKeys(); entity.getSyncedMetaKeys();Локальные сущности (только клиент)
Создаются и живут только на клиенте, без network ID и без синхронизации с сервером. Полезны для декоративных педов, объектов и транспорта.
const ped = new varion.LocalPed(model, { x, y, z }, { x, y, z });
const obj = new varion.LocalObject(model, { x, y, z }, { x, y, z }, /* noOffset */ false);
const veh = new varion.LocalVehicle(model, { x, y, z }, { x, y, z });
obj.pos = { x, y, z }; // pos / rot / frozen / collision / visible — get/set
obj.frozen = false;
obj.destroy();Про
scriptID: сразу после конструктораscriptIDравен0, аidотрицательный (локальный идентификатор). Как только движок закоммитит сущность в мир,scriptIDстановится ненулевым GTA-хендлом, и срабатывают событияgameEntityCreate/baseObjectCreate. Проверяйтеentity.isSpawnedили ждитеgameEntityCreate, прежде чем звать натив-хендлы поscriptID.
varion.WeaponObject(model, pos, rot, ammo, showWorldModel) — отдельный локальный объект-оружие с методом destroy().
Мировые объекты
Классы Blip, Marker, Checkpoint, ColShape, TextLabel — статики all, count, get(id), getByID(id), getByRemoteID(id) (у Blip ещё getByScriptID).
const blip = varion.Blip.get(id);
blip.sprite = 1; blip.color = 5; blip.scale = 1.2; // get/set
blip.name = 'Shop'; blip.shortRange = true; blip.route = false;
blip.pos = { x, y, z }; // + radius/width/height, isRadius/isArea
const mk = varion.Marker.get(id);
mk.markerType = 1; mk.scale = 2; mk.color = { r, g, b, a };
const cp = varion.Checkpoint.get(id);
cp.checkpointType = 1; cp.radius = 3; cp.height = 2; cp.color = { r, g, b, a };
const cs = varion.ColShape.get(id); // colshapeType / radius / height /
// halfExtents / points / pos / dimension
// Локальный 3D-текст:
const label = new varion.TextLabel('Hello', 0, { x, y, z }, 1, { r:255,g:255,b:255,a:255 }, true, 0);
label.destroy();Глобальная и локальная мета
// Локальная (синтетическая, только клиент)
varion.setMeta(key, value); varion.getMeta(key); varion.hasMeta(key);
varion.deleteMeta(key); varion.getMetaKeys();
// Синхронизированная сервером глобальная (read-only на клиенте)
varion.getSyncedMeta(key); varion.hasSyncedMeta(key); varion.getSyncedMetaKeys();
// Локальная мета игрока
varion.setLocalMeta(key, value); varion.getLocalMeta(key);
varion.hasLocalMeta(key); varion.deleteLocalMeta(key); varion.getLocalMetaKeys();
// Запрос установки stream-synced меты (уходит на сервер)
varion.setStreamSyncedMeta(key, value);
varion.deleteStreamSyncedMeta(key);Голос
varion.Voice.volume = 1; // 0–2
varion.Voice.muteInput = true; // заглушить свой микрофон
varion.Voice.activationLevel = 37; // 0–100 (voice-activation)
varion.Voice.noiseSuppressionEnabled = true;
varion.Voice.inputDevice = deviceUuid;
varion.Voice.activityInputEnabled = true;
varion.Voice.toggleInput(true); // push-to-talk вручную
varion.Voice.mutePlayer(player); varion.Voice.unmutePlayer(player);
varion.Voice.isPlayerTalking(player); varion.Voice.isPlayerMuted(player);
varion.Voice.getPlayersInChannel(channelId);
varion.Voice.getAvailableInputDevices();
varion.Voice.clearLocallyMuted();
const ch = varion.Voice.getChannel(channelId); // или varion.VoiceChannel.getByID(id)
ch.spatial; ch.maxDistance; ch.volume; ch.members; ch.muted; ch.filter;
ch.getPlayers(); ch.isActive();
const filter = varion.AudioFilter.create('echo'); // по имени или hash
varion.muteInput(true);
varion.toggleVoiceControls(true);
varion.clearLocallyMuted();Аудио
const snd = varion.Audio.create(varion.assetUrl('mymode', 'sfx/click.wav'), 1);
snd.loop = false; snd.category = 'sfx';
await snd.play(); snd.pause(); snd.stop(); snd.seek(2);
snd.setVolume(0.5); snd.destroy();
snd.playing; snd.currentTime; snd.duration; snd.playbackRate;
varion.Audio.playFrontend(url, 1); // фронтенд-звук (2D)
// 3D-звук: добавьте world-output
snd.addOutput(new varion.AudioOutputWorld({ x, y, z }, 50));
const cat = varion.AudioCategory.get('sfx'); cat.volume = 0.8;HTTP
const res = await varion.Http.get('/server-info.json');
// { ok, status, statusCode, url, text, body, headers }
const data = await varion.Http.getJson('/data.json'); // + res.json
const r = await varion.fetch('/api', { method: 'POST', body: JSON.stringify({}) });
const client = varion.HttpClient.create();
client.setExtraHeader('X-Auth', token);
await client.get(url); // + head/post/put/delete/patch/options/connect/traceЗапросы разрешены только на host/port из manifest-http ресурса или из его asset-pack; игровой порт 7788 заблокирован.
Ресурсы
varion.Resource.all; // все ресурсы
varion.Resource.current; // текущий (в контексте выполнения)
varion.Resource.getByName('mymode'); // или varion.Resource.get('mymode')
varion.Resource.resolveAsset('mymode', 'ui/index.html');
varion.Resource.getPath('mymode', 'client/main.js');
varion.getResources(); // = Resource.all
varion.getResourceConfig('mymode'); // объект config из манифеста
varion.getResourcePath('mymode', 'client/main.js');
varion.hasResource('mymode');
// Экспорты между ресурсами (client-local, без сервера)
varion.exports.foo = () => 42; // регистрируем на своём ресурсе
varion.exports = { foo, bar }; // или пачкой
varion.getResourceExports('other').foo(); // вызываем чужой экспорт
const other = varion.require('other'); // бросит, если ресурс не найденURL и ассеты
varion.httpUrl('cef/index.html'); // базовый HTTP-сервер ресурса
varion.assetUrl('mymode', 'ui/index.html'); // asset-pack ресурса
varion.streamUrl('mymode', 'vehicles/adder.ytd'); // stream-ассеты ресурса
varion.streamCachePath('mymode', 'file.ytd'); // путь в локальном stream-кэшеХранилище
varion.LocalStorage.set('token', 'value');
varion.LocalStorage.get('token');
varion.LocalStorage.has('token');
varion.LocalStorage.delete('token'); // = remove
varion.LocalStorage.clear(); // = deleteAll
varion.LocalStorage.save(); // персист на дискLocalStorage автоматически сохраняется при resourceStop.
Файлы
await varion.File.write('cache:save.json', JSON.stringify(data));
const text = await varion.File.read('cache:save.json', 'utf8');
await varion.File.exists('cache:save.json'); // → boolean
await varion.File.delete('cache:save.json');
varion.File.resolvePath('resource:mymode/data.json');
varion.readFile(path); // алиасы
varion.writeFile(path, data);Камера и экран
varion.getCamPos(); // { x, y, z }
varion.getCamRot(); // { x, y, z }
varion.getCamFov(); // number
varion.getCamForwardVector(); // { x, y, z }
varion.getScreenResolution(); // { x, y }
varion.screenToWorld(0.5, 0.5, 100); // синхронно (луч)
await varion.screenToWorldAsync(0.5, 0.5, 500); // точный game-thread pick
await varion.worldToScreenAsync(x, y, z); // { x, y, z } нормализ.
varion.worldToScreen(x, y, z); // синхронно (из кэша)
varion.copyToClipboard('text');Статистика (stats)
await varion.getStat('MP0_CASH'); await varion.setStat('MP0_CASH', 1000);
await varion.getStatFloat('name'); await varion.setStatFloat('name', 1.5);
await varion.getStatBool('name'); await varion.setStatBool('name', true);
varion.resetStat('name'); varion.resetAllStats(0);
varion.getPackedStat(index, 0); varion.setPackedStat(index, value, 0);
varion.getPackedStatBool(index, 0); varion.setPackedStatBool(index, value, 0);getStat* / setStat* работают через game-bridge и возвращают Promise.
Разрешения и снимки экрана
varion.Permission; // { None:0, ScreenCapture:1, WebRTC:2, Microphone:3, Clipboard:4, All:255 }
varion.PermissionState; // { Allowed:0, Denied:1, Unspecified:2, Failed:3 }
varion.getPermissionState(varion.Permission.ScreenCapture);
varion.hasPermission(varion.Permission.Microphone);
const state = await varion.requestPermission(varion.Permission.ScreenCapture);
const base64 = await varion.takeScreenshot(); // весь экран (base64 PNG)
const gameOnly = await varion.takeScreenshotGameOnly(); // только игровой кадрtakeScreenshot при необходимости сам запросит разрешение ScreenCapture.
WebSocket и Worker
const ws = new varion.WebSocketClient('wss://example/path');
ws.on('open', () => ws.send('hi'));
ws.on('message', (data) => {});
ws.on('close', (code, reason) => {});
ws.autoReconnect = true;
ws.addSubProtocol('json'); ws.setExtraHeader('X-Auth', token);
ws.start(); ws.stop(); ws.destroy();
varion.WebSocketReadyState; // { CONNECTING, OPEN, CLOSING, CLOSED }
const worker = new varion.Worker(varion.assetUrl('mymode', 'worker.js'));
worker.on('message', (data) => {});
worker.start(); worker.pause(); worker.resume(); worker.destroy();Discord
varion.Discord.isReady(); // готовность
varion.Discord.currentUser; // { id, name, discriminator, avatar } | null
varion.Discord.updateActivity({ details: 'In game', state: 'Freeroam', largeImageKey: 'logo' });
varion.Discord.getActivity();
varion.Discord.clearActivity();
await varion.Discord.requestOAuth2Token(appId);
varion.on('discord:ready', (user) => {});Handling и MapZoom
const hd = varion.HandlingData.getForName('adder'); // или .getForHash(hash)
hd.acceleration; hd.brakeForce; hd.maxTraction; /* ... */
hd.setField('acceleration', 0.5);
await hd.applyTo(vehicle);
varion.getVehicleHandling(vehicle);
varion.HandlingData.reloadVehiclePhysics(modelHash);
const zoom = varion.MapZoomData.get('MAIN_MAP');
zoom.fZoomScale = 1.2;
varion.MapZoomData.resetAll();Сервер, соединение и диагностика
varion.getServerHost(); varion.getServerIp(); varion.getServerPort(); varion.getServerName();
varion.getPing();
varion.isConnected();
varion.getConnectionPhase(); // 'idle' | 'ready' | ...
varion.isPaused(); varion.isMenuOpen(); varion.isRadarHidden();
varion.getFps(); varion.getTotalPacketsSent(); varion.getTotalPacketsLost();
varion.getLocale(); // varion.locale
varion.LicenseType; // { None, Rockstar, Steam, Epic, Xbox }
varion.getLicenseType(); varion.getLicenseHash();
varion.getGxtText('key'); // текст GXT-меткиПогода и config-флаги
varion.setWeatherCycle(['CLEAR', 'RAIN'], [10, 5]);
varion.setWeatherSyncActive(true);
varion.setConfigFlag('flagName', true);
varion.getConfigFlag('flagName');
varion.loadYtyp('name'); varion.unloadYtyp('name');Утилиты
varion.Utils — векторная математика и хелперы:
varion.Utils.distance(a, b); varion.Utils.squaredDistance(a, b);
varion.Utils.vectorLength(v); varion.Utils.normalize(v);
varion.Utils.dot(a, b); varion.Utils.cross(a, b);
varion.Utils.add(a, b); varion.Utils.sub(a, b);
varion.Utils.mul(v, s); varion.Utils.divide(v, s); varion.Utils.neg(v);
varion.Utils.lerp(a, b, t); varion.Utils.clamp(v, min, max);
varion.Utils.degToRad(d); varion.Utils.radToDeg(r);
varion.Utils.randomInt(min, max); varion.Utils.isNaN(v);
await varion.Utils.wait(500); // Promise-задержка
await varion.Utils.registerPedheadshotBase64(ped); // + registerPedheadshot3Base64 / *Transparent*
const b64 = await varion.getHeadshotBase64(headshotId);
const p = new varion.Profiler();
p.timeStart('x'); const ms = p.timeEnd('x');
varion.Profiler.fps; varion.Profiler.tickMs; varion.Profiler.nativeCalls;
varion.getGamepads(); // массив Gamepad
varion.Gamepad.get(0); // .connected / .axisLeftX / .isButtonPressed(btn)UI: WebView и RmlDocument
Полный справочник по WebView — на странице WebView.
const view = varion.WebView.create({ url: varion.assetUrl('ui-pack', 'index.html'), overlay: true });
view.visible = true;
view.on('hud:ready', () => {});
view.emit('hud:setHealth', 200);
// RmlDocument — HTML/WebView-бэкенд (нативного RmlUi-рендерера нет; .html грузится
// в WebView, .rml показывает уведомление):
const doc = new varion.RmlDocument(varion.assetUrl('ui-pack', 'menu.html'));
doc.show(false, true); doc.hide(); doc.update();Stream-события
varion.on('stream:ready', (...mounts) => {
// по одному аргументу на каждый примонтированный ресурс
for (const mount of mounts) varion.log(`${mount.name} -> ${mount.streamBase}`);
});
varion.on('stream:updated', (...mounts) => {});
varion.on('stream:mounted', (stats) => {}); // { registered, existing, failed, ... }Каждый mount — объект { name, streamBase } (IStreamResourceMount). Это не строка с именем ресурса; при полном ремаунте приходят все ресурсы сразу (для одного ресурса — один объект).
GTA Natives
// Вызов по имени (camelCase)
varion.natives.setEntityVisible(scriptId, true, false);
varion.natives.freezeEntityPosition(scriptId, true);
// Вызов по хешу
varion.natives.invoke('0x262B14F48D29DE80', pedHandle, 11, 1, 0, 0);
varion.natives.invokeFloat('0xEEF059FAD016D209', pedHandle);
varion.natives.invokeVector3('0x1EB9E7DF7E839CAF', x, y, z, heading, rx, ry, rz);
// Реальное возвращаемое значение — через async-варианты
const health = await varion.natives.invokeAsync('GET_ENTITY_HEALTH', scriptId);varion.game — тот же bridge плюс joaat. Синхронный invoke для большинства read-нативов возвращает placeholder; за реальным значением используйте invokeAsync / invokeFloatAsync / invokeStringAsync / invokeVector3Async. Подробнее — на странице GTA natives.
Client JS нельзя считать trusted с точки зрения gameplay/security. Важные gameplay-, economy- и permission-действия проверяйте на сервере.
Source backing
Страница построена по runtime-файлам sdk/client-js-runtime/injected/core-bootstrap.js, events-bootstrap.js, entities-bootstrap.js, services-bootstrap.js, world-bootstrap.js, interaction-bootstrap.js, platform-bootstrap.js, dispatch-bootstrap.js, mvalue-bootstrap.js и client/src/stream/CStreamResourceClient.cpp.