事件与数据模型
事件信封
json
{
"v": 1,
"type": "round.open",
"table": "B001",
"seq": 1532,
"prev": 1531,
"ts": "2026-09-24T03:00:30.000Z",
"game": "baccarat",
"shoe": "260924-03",
"round": 12,
"roundKey": "B001-260924-03-12",
"data": {}
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
v | int | 是 | 协定主版本,目前为 1;更大的版本回 UNSUPPORTED_VERSION |
type | string | 是 | 事件类型(见下表);本平台还不认得的类型会 ack 后忽略 |
table | string | 是 | 来源桌号,[A-Za-z0-9_.:-]{1,64} |
seq | int | 是 | 视频流内严格递增,≥ 1,可以不连号 |
prev | int | 是 | 同视频流上一笔事件的 seq;第一笔为 0;必须小于 seq |
ts | string | 是 | 来源端事件时间,UTC ISO-8601 含毫秒(2026-09-24T03:00:30.000Z),且必须是存在的日期。来源主机必须以 NTP 校时 |
game | string | 已知类型必填 | baccarat、dragontiger 或 niuniu |
shoe | string | 靴、局事件必填 | 来源靴号,[A-Za-z0-9_.:-]{1,64};同一张桌内永久不重复(建议含日期)。牛牛没有靴,这里是场次(见牛牛) |
round | int | 局事件必填 | 本靴(牛牛为本场次)第几局,1–100000 |
roundKey | string | 局事件必填 | 同一张桌内永久唯一,[A-Za-z0-9_.:-]{1,128};同一局的修正与作废沿用 |
data | object | 是 | 依类型而定 |
- 未知的字段一律忽略;添加字段属于兼容变更,不升主版本。
- 「靴事件」:
shoe.start、shoe.end;「局事件」:round.open、round.close、card、reveal、round.result、round.correct、round.cancel(局事件也需要shoe)。
事件目录
type | 时机 | data |
|---|---|---|
table.info | 连接后每桌送一次;内容变更时再送 | {name, game, decks?, dealer?, video?[], capabilities: {cardByCard, countdown, snapshot}} |
table.status | 状态改变 | {status: "open"|"paused"|"maintenance"|"closed", reason?} |
table.snapshot | 无法重送时,或重连后主动对齐 | {status, phase: "idle"|"betting"|"closed"|"result", shoe?, round?, roundKey?, closesAt?, history: [{round, result}], gap} |
shoe.start | 新靴开始 | {decks?} |
shoe.end | 靴结束(选用) | {rounds} |
round.open | 开放下注 | {closesAt?, betSeconds?, lastInShoe?, dealer?} |
round.close | 停止下注 | {} |
card | 发出一张牌(选用,需 cardByCard 能力) | {pos, card, faceDown?} |
reveal | 牛牛逐家开牌(选用) | {hands: ["P1"]} |
round.result | 开牌结果(第一版) | {rev: 1, complete, result} |
round.correct | 修正已发布的结果 | {rev: n+1, complete, result, reason} |
round.cancel | 本局作废 | {reason: "misdeal"|"dealer_error"|"device_error"|"table_closed"|"other", note?} |
dealer.change | 换荷官 | {dealer: {id, name, photoUrl?}} |
本平台的处理:
type | 处理 |
|---|---|
table.info | 更新桌台目录;未绑定的来源桌列为「已发现、未绑定」 |
table.status | 非 open 时立即停止收注 |
table.snapshot | 以快照重建状态与本靴路单 |
shoe.start | 清空路单、通知玩家换靴 |
round.open | 开局并计算停止下注时间(见时间与下注时窗);lastInShoe: true 让游戏提示「本局后换靴」 |
round.close | 立即停止下注 |
card | 若仍在下注中立即停止下注;推播开牌动画。牛牛带 faceDown: true 的牌不公开牌面,只通知玩家「发了一张到这个牌位」 |
reveal | 公开这几家暗发的牌(前台逐家翻牌);只用于牛牛 |
round.result | 验证后结算 |
round.correct | 依来源信任等级与护栏自动重算,或维持原结果并列为争议局(见信任等级) |
round.cancel | 结算前:全额退款;结算后:规则同 round.correct |
dealer.change | 显示在游戏画面 |
字段限制:name 1–128 字符;decks 1–16;betSeconds 1–600;dealer.id 1–64 字符、dealer.name 1–128 字符、dealer.photoUrl 必须是 https://;video 最多 8 笔,每笔 {protocol: "webrtc"|"flv"|"hls"|"agora"|"whep", url?, quality?: "sd"|"hd", auth: "none"|"token"|"referer"};reason、note 最多 500 字符;history 最多 200 笔;closesAt 为含毫秒的 UTC 时间;faceDown 为布尔值;reveal 的 hands 为 1–4 个不重复的 B、P1、P2、P3。
补充规则:
- 同一个
roundKey在下注中再送一次round.open,视为更新倒数(例如荷官重设计时);停止下注后才送达就忽略。 - 没有
round.open就直接收到结果(例如来源中途接上)时,只更新路单,该局不接受下注。 - 不能以第二个
round.result修改结果:必须用round.correct并递增rev(round.result的rev固定为 1,round.correct的rev≥ 2,而且必须有reason)。同一个rev送出不同的结果时,本平台维持原结果并记录为冲突。
游戏数据模型
牌面编码
<点数><花色>:点数 A 2 3 4 5 6 7 8 9 10 J Q K,花色 S 黑桃、H 红心、D 方块、C 梅花。例如 AS、10H、QD。
百家乐 result
json
{
"cards": { "P1": "2C", "B1": "4H", "P2": "3D", "B2": "2S", "P3": "10S" },
"player": 5,
"banker": 6,
"winner": "B",
"playerPair": false,
"bankerPair": false
}- 发牌位置
P1 B1 P2 B2 P3 B3,未发的牌省略。 player、banker为点数 0–9;winner为P、B、T(和)。complete: true时必须有完整牌面(至少P1 B1 P2 B2)。本平台会重算点数、输赢、对子,并检查补牌规则,不一致时回RESULT_INCONSISTENT,再依信任等级处理。complete: false时可以只给player、banker、winner、playerPair、bankerPair,选填cardCount(两家合计张数 4–6)。- 上例是庄 6 点胜:闲 2+3=5 补 10 仍为 5;庄 4+2=6,闲第三张为 0,庄不补。
龙虎 result
json
{ "cards": { "D": "KH", "T": "9S" }, "winner": "D" }winner:D龙、T虎、TIE和。- 点数由牌面决定:A=1 … K=13(A 最小),不比花色;点数相同且花色相同为同花和。
complete: false时可以只给winner;这种情况下同花和与大小单双(看各方的牌)无法判定,这些注会退款。先前已送过的card事件仍会用来判定。card事件的位置为D、T。
牛牛 result
json
{
"cards": {
"F": "7H",
"B-1": "KS", "B-2": "QD", "B-3": "10C", "B-4": "3S", "B-5": "5H",
"P1-1": "4C", "P1-2": "6D", "P1-3": "JS", "P1-4": "9C", "P1-5": "KH",
"P2-1": "2D", "P2-2": "8S", "P2-3": "QC", "P2-4": "AH", "P2-5": "2C",
"P3-1": "7D", "P3-2": "3H", "P3-3": "KD", "P3-4": "5C", "P3-5": "5D"
},
"hands": { "B": 8, "P1": 9, "P2": 3, "P3": 10 },
"winners": { "P1": "P", "P2": "B", "P3": "P" }
}- 牌位:头牌
F;庄B-1~B-5;闲一P1-1~P1-5,闲二P2-1~P2-5,闲三P3-1~P3-5(-n是发到那一家的第几张)。 hands:各家牌型代码,0无牛、1–9牛几、10牛牛、11五花牛、12炸弹、13五小牛(11–13 只在打开特殊牌型的桌使用)。winners:P1、P2、P3各自对庄,P闲赢、B庄赢(没有和局)。牌型与比牌规则见桌台与局结果。complete: true时必须有 21 张(头牌+四家各 5 张),hands可以省略。complete: false时至少要有winners(平倍就能结算)。翻倍要有赢的那一手的牌型才能结算,所以请尽量附上hands,没有时翻倍的注退款。先前已送过的card事件仍会用来判定。- 本平台一律依牌面重算牌型与输赢,来源声明的
hands、winners只拿来比对。检查项目:21 张不重复(一局一副牌)、牌型与输赢符合牌面(特殊牌型关闭的桌出现 11–13 也算不符);逐张送card时另外检查头牌最先发、之后第 n 张的牌位照头牌决定的顺序、同一个牌位不重发、结果的牌与已发出的牌相同。任何一项不符都视为RESULT_INCONSISTENT,依信任等级处理。 - 发牌顺序:头牌点数 A 5 9 K 从庄先发、2 6 10 从闲一、3 7 J 从闲二、4 8 Q 从闲三,之后照 庄 → 闲一 → 闲二 → 闲三 轮流,每家 5 张。上例头牌 7♥,所以依序发到
P2-1、P3-1、B-1、P1-1、P2-2…;庄牛8,闲一牛9 赢、闲二牛3 输、闲三牛牛 赢。 - 场次:每局一副新牌、没有靴,以
shoe当场次(例如每 60 局或换荷官时送shoe.start换一场),round是本场次第几局;table.info的decks建议填1。
发牌与开牌的事件(建议逐张送,table.info 的 capabilities.cardByCard 为 true):
round.open→round.close(第一张牌送达时也会立即停止下注)。- 头牌:
card {"pos": "F", "card": "7H"}。头牌一律是明牌,F不能带faceDown: true。 - 20 张依头牌决定的顺序发出,面朝下的牌带
faceDown: true:card {"pos": "P2-1", "card": "2D", "faceDown": true}。本平台收下牌面但不公开,只通知玩家「发了一张到闲二」;不带faceDown的牌就是明牌,照常显示。 - 逐家开牌:
reveal {"hands": ["P1"]},建议依 闲一 → 闲二 → 闲三 → 庄 各送一次(一次也可以开好几家);本平台公开这几家的牌。reveal只用于牛牛(其他游戏回INVALID_EVENT),和其他局事件一样需要shoe、round、roundKey。 round.result(complete: true,21 张)。还没开的家,本平台会依 闲一 → 闲二 → 闲三 → 庄 补开后再公开结果。
时间与下注时窗
- 来源的
closesAt是权威的下注截止时间。本平台实际停止下注的时间取下列三者最早的一个,再提前约 1 秒(吸收玩家端延迟):closesAt加上时钟偏移估计- 收到
round.open的时间加上桌设置的最长下注秒数 - 收到
round.close或第一张card的时间
- 来源没有
countdown能力时,以betSeconds或桌设置的秒数计时。 - 时钟同步:心跳带来源时间,本平台据此估算时钟偏移并监控延迟。停止下注的时间依赖正确的时钟,请务必以 NTP 校时。