Голос
Varion передаёт голос по выделенному голосовому каналу транспорта. Сервер — источник истины и «регулировщик»: он создаёт голосовые каналы, хранит их состав, ретранслирует голос между участниками одного канала и для пространственных каналов отсекает слушателей по дистанции. Клиент управляет только локальным воспроизведением и захватом (громкость, свой микрофон, устройство, порог активации, шумоподавление, локальный mute); он не может создавать каналы или переносить игроков между ними.
Аудио непрозрачно для сервера: кадры голоса ретранслируются как есть и никогда не декодируются и не перекодируются. Поддерживаются кодеки PCM и Opus, формат — 48 кГц, кадр 20 мс.
Модель
| Сторона | Что делает |
|---|---|
| Сервер | создаёт и уничтожает каналы, добавляет и убирает участников, авторитетный mute говорящего, приоритет, фильтр канала, ретрансляция и отсечение по дистанции |
| Клиент | локальная громкость воспроизведения, mute своего микрофона, выбор устройства захвата, порог активации, шумоподавление, локальный mute отдельного игрока, чтение состояния каналов |
Голосовой канал принадлежит серверному ресурсу, который его создал, и уничтожается при stop/restart этого ресурса — ручная очистка не нужна. Клиент начинает передавать голос только после того, как сервер добавит его в канал (этот канал становится активным). При отключении игрок автоматически убирается из всех каналов.
Серверная сторона
Создание канала
varion.createVoiceChannel(spatial, maxDistance?) возвращает VoiceChannel — это эквивалент new varion.VoiceChannel(spatial, maxDistance). Вызывается в контексте ресурса (иначе бросает исключение).
spatial = true— пространственный канал: слушатель слышит говорящего только в пределахmaxDistance(3D-дистанция по позициям игроков).maxDistanceпо умолчанию50, допустимый диапазон1..10000.spatial = false— непространственный канал: все участники слышат всех, дистанция не учитывается (maxDistanceравен0).
const proximity = varion.createVoiceChannel(true, 20); // пространственный, 20 м
const team = varion.createVoiceChannel(false); // командный, без дистанцииСвойства VoiceChannel
| Свойство | Тип | Доступ | Назначение |
|---|---|---|---|
id | number | read-only | id канала, выделяется сервером |
type | 'voiceChannel' | read-only | тип объекта |
valid | boolean | read-only | жив ли канал |
isSpatial | boolean | read-only | пространственный ли канал |
maxDistance | number | read-only | радиус слышимости (0 для непространственного) |
players | Player[] | read-only | текущие участники как proxy Player |
playerCount | number | read-only | число участников |
priority | number | read/write | приоритет при пересечении каналов (по умолчанию 0) |
filter | number | read/write | hash аудиофильтра канала (0 — без фильтра) |
Методы VoiceChannel
| Метод | Назначение |
|---|---|
addPlayer(player) | добавить игрока в канал |
removePlayer(player) | убрать игрока из канала |
hasPlayer(player) | состоит ли игрок в канале (алиас isPlayerInChannel) |
isPlayerInChannel(player) | состоит ли игрок в канале |
isPlayerMuted(player) | заглушён ли игрок на сервере |
mutePlayer(player, muted?) | серверный mute (по умолчанию true; false = снять) |
unmutePlayer(player) | снять серверный mute |
setFilter(filterHash) | задать фильтр (то же, что filter = filterHash) |
destroy() | уничтожить канал |
Аргумент player принимает proxy Player или числовой id.
Статические члены: VoiceChannel.all (все валидные каналы), VoiceChannel.getByID(id) (канал или null).
Серверный mute (авторитетный)
channel.mutePlayer(player) заглушает исходящий голос указанного участника для всего канала — его перестают слышать все остальные участники. Это серверное, авторитетное решение, в отличие от локального клиентского mute, который влияет только на то, что слышит один игрок. Вызов mutePlayer(player, false) эквивалентен unmutePlayer(player). Замьютить можно только игрока, который уже состоит в канале.
Приоритет
Если слушатель состоит с говорящим сразу в нескольких каналах, сервер выбирает один канал для доставки голоса: сначала — с наибольшим priority; при равенстве предпочитается непространственный канал; при дальнейшем равенстве — канал с меньшим id. Так priority определяет, чей голос «побеждает» при пересечении каналов (например, командный канал важнее общего proximity).
Фильтры
channel.setFilter(hash) или channel.filter = hash задаёт hash аудиофильтра. Сервер передаёт этот hash клиентам, а DSP применяется на клиенте к входящему голосу этого канала. Хеши считаются через varion.hash(name) (JOAAT, нижний регистр). Встроенные пресеты:
| Имя | Эффект |
|---|---|
walkietalkie | полосовой фильтр (рация) |
phone | полосовой фильтр (телефон) |
underwater | фильтр нижних частот (под водой) |
Любой другой hash доставляется клиентам, но без встроенного эффекта — голос проходит без изменений. channel.filter = 0 снимает фильтр.
const radio = varion.createVoiceChannel(false);
radio.setFilter(varion.hash('walkietalkie'));
radio.priority = 10;Как сервер ретранслирует голос
- Клиент загружает один кадр микрофона; состав
VoiceChannelрешает, кто его услышит. Указанный клиентом id канала сохраняется только для совместимости — маршрут определяет членство в каналах на сервере. - Аудио непрозрачно: сервер не декодирует кадры, только проверяет их размер и ретранслирует.
- Для пространственных каналов сервер отсекает участников за пределами
maxDistance. - Кадры голоса идут по ненадёжному каналу транспорта, а изменения состава и конфигурации каналов — по надёжному.
Пример (сервер)
const proximity = varion.createVoiceChannel(true, 15);
varion.on('playerReady', (player) => {
proximity.addPlayer(player); // сервер решает, кто в каком канале
});
varion.onClient('voice:report', (reporter, targetId) => {
proximity.mutePlayer(targetId); // авторитетно глушим для всего канала
});
// Явно убирать игрока при отключении не нужно — он снимается со всех каналов сам.Клиентская сторона
Клиент управляет локальным воспроизведением и захватом через varion.Voice. Ни один клиентский вызов не меняет состав канала — это делает только сервер.
varion.Voice — воспроизведение и захват
| Член | Тип / сигнатура | Назначение |
|---|---|---|
activeChannel | number (get) | id активного канала |
pushToTalk | boolean (get) | удерживается ли клавиша PTT сейчас |
volume | number (get/set) | локальная громкость воспроизведения, 0..2 |
muteInput | boolean (get/set) | заглушить свой микрофон |
inputMuted | boolean (get) | алиас muteInput |
activationLevel | number (get/set) | порог голосовой активации, 0..100 |
noiseSuppressionEnabled | boolean (get/set) | шумоподавление |
inputDevice | string | null (get/set) | id устройства захвата |
activityInputEnabled | boolean (get/set) | режим голосовой активации (вместо PTT) |
activationKey | number (get) | клавиша PTT |
voiceControlsEnabled | boolean (get) | включены ли встроенные голосовые контролы |
toggleInput(enabled) | — | вручную открыть или закрыть микрофон |
getAvailableInputDevices() | { name, uuid }[] | устройства захвата (uuid равен null для устройства по умолчанию) |
Игроки и mute (клиент)
| Метод | Назначение |
|---|---|
getChannel(channelId?) | клиентский VoiceChannel (по умолчанию активный) или null |
getPlayersInChannel(channelId?) | Player[] в канале (по умолчанию активном) |
isPlayerTalking(player) | говорит ли игрок сейчас |
isPlayerMuted(player) | заглушён ли игрок на сервере в активном канале |
isPlayerLocallyMuted(player) | заглушён ли игрок локально |
mutePlayer(player) | локально приглушить воспроизведение игрока и отправить varion:voice:mute на сервер |
unmutePlayer(player) | снять локальный mute и уведомить сервер |
clearLocallyMuted() | очистить локальный список mute |
Локальный mute влияет только на то, что слышит этот клиент; серверный mute (isPlayerMuted) авторитетен. Метод Voice.mutePlayer дополнительно отправляет серверное событие varion:voice:mute (channelId, playerId, muted), которое ресурс может обработать через varion.onClient — само по себе оно не является авторитетным серверным mute.
Клиентский VoiceChannel (только чтение)
Получить снимок канала можно через varion.Voice.getChannel(id) или varion.VoiceChannel.getByID(id).
| Член | Назначение |
|---|---|
id | id канала |
spatial | пространственный ли канал |
maxDistance | радиус слышимости (по умолчанию 50) |
volume (get/set) | громкость воспроизведения этого канала, 0..2 |
members | массив id участников |
muted | массив id заглушённых на сервере |
filter | AudioFilter или null |
getPlayers() | Player[] участников |
isActive() | активный ли это канал |
Статический член: VoiceChannel.getByID(id).
AudioFilter
varion.AudioFilter.create(nameOrHash) возвращает кэшированный AudioFilter по имени (имя хешируется через JOAAT) или по числовому hash. У объекта есть поле hash.
Утилиты верхнего уровня
varion.muteInput(muted = true)— то же, чтоVoice.muteInput = ....varion.toggleVoiceControls(enabled)— включить или выключить встроенные голосовые контролы.varion.clearLocallyMuted()— то же, чтоVoice.clearLocallyMuted().
Пример (клиент)
varion.Voice.volume = 1.2;
varion.Voice.noiseSuppressionEnabled = true;
varion.Voice.activationLevel = 35;
// Индикатор «говорит» в HUD
varion.on('voice:playerStartTalking', (player) => hud.setTalking(player.id, true));
varion.on('voice:playerStopTalking', (player) => hud.setTalking(player.id, false));
// Локально приглушить надоедливого игрока
varion.Voice.mutePlayer(annoyingPlayer);
// Ручной push-to-talk по клавише N
varion.on('keydown', (key) => { if (key === 0x4E) varion.Voice.toggleInput(true); });
varion.on('keyup', (key) => { if (key === 0x4E) varion.Voice.toggleInput(false); });Голосовые события
События приходят на клиенте; подписка — varion.on(name, callback). Полный контракт событий описан в События и RPC — здесь только краткая сводка.
| Событие | Аргументы | Когда |
|---|---|---|
playerStartTalking / voice:playerStartTalking | (player) | игрок начал говорить |
playerStopTalking / voice:playerStopTalking | (player) | игрок перестал говорить |
voice:channelChange | (previousChannelId, activeChannelId) | сменился активный канал |
voice:channelCreate | (voiceChannel) | клиент узнал о новом канале |
voice:channelDestroy | (channelId) | канал исчез |
voice:pushToTalk | (active) | состояние push-to-talk изменилось |
Смежные страницы
- Server JavaScript API — раздел «Голосовые каналы» в общем справочнике.
- Client JavaScript API — полный клиентский объект
varion. - События и RPC — точные сигнатуры всех событий.
Источник
Страница сверена с sdk/Varion.Server/npm/@varion/server/index.d.ts (класс VoiceChannel, varion.createVoiceChannel), рантаймом Node-host server/host/varion-host.cjs, серверным релеем server/src/CVoiceManager.{h,cpp} и клиентским рантаймом sdk/client-js-runtime/injected/services-bootstrap.js (объект Voice, классы VoiceChannel и AudioFilter). Список голосовых событий — События и RPC.