PeakSelfHostedPhoton Voice Fix

版本 v1.0.0·更新於 2026-08-16

Makes PEAK voice chat work on a self-hosted Photon server. Companion to PeakSelfHostedPhoton. All players need it.

模組介紹 作者原文

PeakSelfHostedPhoton_Voice_Fix

Makes PEAK voice chat work on a self-hosted Photon server.

Companion to PeakSelfHostedPhoton,

which redirects the game to your own Photon Server but does not carry voice.

Install this alongside it and voice works.

Every player needs it. The patch changes the sending side, so a player without

it simply cannot be heard.

Tested on PEAK 2.1.a with Photon Server SDK 4.0.29 (the 2016 build) over

GpBinaryV16, four players, host on a public IP.

What it fixes

Two independent problems. The second only becomes visible once the first is

fixed, which is why this looks like a dead end at first.

  • Protocol16 cannot serialize ByteArraySlice

Self-hosting forces the client onto GpBinaryV16, because a 2016-era server speaks

nothing newer. Photon Voice hands each Opus frame over as a ByteArraySlice, and

Protocol16 has no serializer for that type:

System.Exception: cannot serialize(): ExitGames.Client.Photon.ByteArraySlice

at ExitGames.Client.Photon.Protocol16.Serialize(...)

at Photon.Realtime.LoadBalancingPeer.OpRaiseEvent(...)

at Photon.Voice.LoadBalancingTransport.SendFrame(...)

at POpusCodec.OpusEncoder.Encode(...)

The encode thread dies on the first frame. The game itself is unaffected, since

PEAK's own RPCs use plain types.

The payload is built by Photon.Voice.LoadBalancingTransport2.buildFrameMessage

(not PhotonTransportProtocol.buildFrameMessage - the stack trace shows the base

class because SendFrame is declared there). A postfix copies the slice into a

plain byte and releases the slice back to the pool.

Safe on the receiving end: onVoiceFrameEvent already accepts both shapes, and on

the wire it is a byte array either way. The slice was only a send-side zero-copy

optimisation.

  • Voice frames use a delivery mode old servers drop

With the first fix in, the client reports thousands of successful sends while the

server logs nothing at all from the voice peer:

voice: built=4998 raiseOk=5000 raiseFail=0 state=Joined

SendFrame picks the delivery mode like this:

if (flags & 1) -> Reliable

else if (cppCompatibilityMode) -> Reliable / Unreliable

else -> ReliableUnsequenced / UnreliableUnsequenced

The default path uses the unsequenced modes, which postdate Photon Server

4.0.28 (February 2016). The server drops those commands silently - no error, no

log line. Control operations go over the ordinary reliable channel, which is why

auth and join succeed while every audio frame vanishes.

A prefix on the LoadBalancingTransport2 constructor forces

cppCompatibilityMode = true, the flag that exists for exactly this.

Verifying

BepInEx/LogOutput.log should contain:

Patched Photon.Voice.LoadBalancingTransport2..ctor

Patched Photon.Voice.LoadBalancingTransport2.buildFrameMessage

Forced cppCompatibilityMode=true: voice frames will use Unreliable...

Voice frames are now sent as byte instead of ByteArraySlice (N bytes in the first frame).

The last line appears on the first word anyone speaks. If it never shows up, the

microphone is not producing frames and the problem is elsewhere.

cannot serialize(): ByteArraySlice should be gone from the game's Player.log.

Configuration

BepInEx/config/peak.selfhostedphoton.voicefix.cfg

| Setting | Default | What it does |

|---|---|---|

| Diagnostics / LogFrameCounters | false | Logs frames built, sends accepted, sends refused and client state every 250 frames. Only for working out why voice is silent - it writes a line every few seconds while anyone is talking. |

Compatibility

Both changes are harmless on GpBinaryV18 and on Photon Server 5, so there is no

need to remove this after moving to a newer server.

Cost is one array copy per frame (~50 per second) and giving up unsequenced

delivery. Next to Opus encoding, that is nothing.

Does nothing on the official Photon Cloud - it only matters when the client has

been pushed onto the legacy protocol.

Credits

Diagnosis done by reading the IL of PhotonVoice.API.dll and correlating client

counters against Photon Server o

安裝與使用 安裝:BepInEx 外掛

  1. 本站下載的壓縮包就是模組本體,通常是 BepInEx 外掛(解壓後得到 .dll 或外掛資料夾)。
  2. 建議流程:用社群模組管理器(如 r2modman)選中本遊戲後匯入該模組,管理器會自動裝好 BepInEx 前置並處理依賴。
  3. 手動流程:先在遊戲目錄裝好 BepInEx 載入器,再把解壓出的 .dll 或外掛資料夾放進 BepInEx/plugins/。
  4. 啟動遊戲,BepInEx 控制台會列印載入日誌;確認這個模組出現在日誌裡即為安裝成功。

帶「前置依賴」的模組需要一併安裝,否則不會載入。功能開關通常在遊戲內或外掛自帶的設定檔裡調整。

前置依賴

下面這些內容需要先安裝,否則該模組不會生效:

BepInEx-BepInExPack_PEAKloser1727-PeakSelfHostedPhoton

檔案與校驗

檔案名稱
PeakSelfHostedPhoton_Voice_Fix-1.0.0.zip
檔案大小
11.3 KiB
SHA-256
84b55f7df5451d19aa5d416d11206a35ac445b6b637069a3740e51ebc4517ac9
入庫時間
2026-10-10 18:40