Protocol Details
TazeU embeds a server implementation of the DG-LAB WebSocket v2 protocol, adopting a one-to-many architecture — the Mod acts as the WS Server, and multiple DG-LAB APPs act as WS Clients.
Architecture Overview
┌─────────────────────────────────────┐
│ Slay the Spire 2 (Mod = Server) │
│ │
│ TazeU Mod │
│ ├─ DGLabServer (TcpListener) │
│ └─ ws://localIP:port/clientId │
└──────────┬──────────────────────────┘
│ WebSocket (One-to-many)
┌─────┴─────┐
▼ ▼
┌─────────┐ ┌─────────┐
│ DG-LAB │ │ DG-LAB │ ...N APPs
│ APP #1 │ │ APP #2 │
│ (BLE) │ │ (BLE) │
└────┬────┘ └────┬────┘
▼ ▼
┌─────────┐ ┌─────────┐
│Coyote 3 │ │Coyote 3 │ Respective Hardware
└─────────┘ └─────────┘Each APP bridges to its respective Coyote 3.0 hardware via Bluetooth (BLE). Shock events are broadcast by the Server to all bound clients, but each client maps them independently according to its own channel strength limit.
Connection Flow
Each client independently undergoes the following handshake process:
Server APP (Client)
│ │
│ 1. Generate clientId (Global GUID) │
│ 2. Start TcpListener │
│ │
│◄────────── 3. APP Scans to Connect ─────│
│ ws://ip:port/{clientId} │
│ │
│ 4. TCP Accept → HTTP → WS Handshake │
│ │
│──── 5. Server assigns targetId ────────►│
│ { type: "bind", │
│ clientId, targetId } │
│ │
│◄──── 6. APP replies bind confirm ───────│
│ { type: "bind", message: "200" } │
│ │
│──── 7. Strength to zero ───────────────►│
│ Triggers APP to return channel limit │
│ │
│◄──── 8. Strength Feedback ──────────────│
│ strength-{currentA}+{limitA}+ │
│ {currentB}+{limitB} │
│ │
│ 9. Ready for comms, join broadcast list │
▼ ▼TIP
Scan URL Format: https://www.dungeon-lab.com/app-download.php#DGLAB-SOCKET#ws://{ip}:{port}/{clientId}
The DG-LAB APP parses the anchor part after # to obtain the WS address.
WebSocket Handshake
DGLabServer manually implements the HTTP → WebSocket upgrade handshake, without using .NET's HttpListener. The reason is the network stack limitation of the Godot runtime.
Handshake steps:
TcpListener.AcceptTcpClientAsync()accepts the TCP connection.- Reads the HTTP request headers byte by byte (max 8192 bytes, to prevent malicious overly long headers).
- Extracts
Sec-WebSocket-Key. - Calculates
Sec-WebSocket-Accept(SHA-1 + Base64). - Returns
101 Switching Protocolsresponse. - Uses
WebSocket.CreateFromStream()to enter WS communication mode.
Message Format
All messages are JSON text frames.
Server → Client
bind (Initial Binding):
{
"type": "bind",
"clientId": "server-guid",
"targetId": "assigned-target-id",
"message": "200"
}msg (Instruction Passing):
{
"type": "msg",
"clientId": "server-guid",
"targetId": "target-id",
"message": "strength-1+1+2+2"
}heartbeat:
{
"type": "heartbeat",
"clientId": "server-guid",
"targetId": "target-id",
"message": "200"
}error:
{
"type": "error",
"clientId": "server-guid",
"targetId": "target-id",
"message": "403"
}Client → Server
bind (Binding Confirmation):
{
"type": "bind",
"message": "200"
}msg (Message Report):
{
"type": "msg",
"message": "strength-{currentA}+{limitA}+{currentB}+{limitB}"
}Instruction Format
All business instructions are passed through the msg.message field in a plain text string format.
Strength Control
strength-{channelA}+{channelB}+{channelA}+{channelB}- The first two values are strength delta (positive increases, negative decreases,
0remains unchanged). - The last two values are direct setting values (
0means not setting).
DGLabProtocol.StrengthCommand implementation:
// Example: Channel A set to 50, Channel B set to 30
StrengthCommand(50, 30) → "strength-0+0+50+30"Pulse Waveform
pulse-{channel}:["hex1","hex2",...]channelis"A"or"B".- The HEX array is V3 format Waveform data (detailed below).
- The APP's internal queue caches up to 500 items (50 seconds), with a maximum of 100 items per array (10 seconds).
Clear Waveform
clear-{channel}Clears the Waveform queue for the specified channel.
V3 Waveform Format
Each Waveform data is 16 hexadecimal characters (8 bytes), representing 100ms of output, containing 4 subsets × 25ms sub-pulses:
[freq1][freq2][freq3][freq4][int1][int2][int3][int4]