@zhuweiyou/dsh-holdem 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -20
- package/lib/client.js +331 -283
- package/lib/client.js.map +1 -1
- package/lib/index.js +41 -10
- package/lib/index.js.map +1 -1
- package/lib/types/client/ActionBar.d.ts +6 -1
- package/lib/types/client/ActionBar.d.ts.map +1 -1
- package/lib/types/client/Table.d.ts.map +1 -1
- package/lib/types/client/i18n.d.ts.map +1 -1
- package/lib/types/client/store.d.ts.map +1 -1
- package/lib/types/config.d.ts +5 -0
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/invariant.d.ts +6 -1
- package/lib/types/invariant.d.ts.map +1 -1
- package/lib/types/net/ws.d.ts.map +1 -1
- package/lib/types/room/RoomManager.d.ts +10 -0
- package/lib/types/room/RoomManager.d.ts.map +1 -1
- package/lib/types/types.d.ts +2 -0
- package/lib/types/types.d.ts.map +1 -1
- package/lib/worker.js +41 -9
- package/lib/worker.js.map +1 -1
- package/package.json +1 -1
- package/worker/index.ts +3 -0
package/lib/index.js
CHANGED
|
@@ -15,7 +15,10 @@ const PACKAGE_NAME = "@zhuweiyou/dsh-holdem";
|
|
|
15
15
|
const SUBPROTOCOL = "holdem.v1";
|
|
16
16
|
const ROUTE_PREFIX = "/holdem";
|
|
17
17
|
const MAX_SEATS = 6;
|
|
18
|
-
|
|
18
|
+
/** 观战席位默认值;实际值由设置项 maxSpectators 决定。 */
|
|
19
|
+
const DEFAULT_MAX_SPECTATORS = 16;
|
|
20
|
+
/** 设置项 maxSpectators 能配到的最大值。 */
|
|
21
|
+
const SPECTATORS_LIMIT = 64;
|
|
19
22
|
const MAX_ROOMS = 8;
|
|
20
23
|
const MAX_CHAT_LINES = 80;
|
|
21
24
|
const MAX_HISTORY_HANDS = 50;
|
|
@@ -24,6 +27,8 @@ const MAX_FRAME_BYTES = 64 * 1024;
|
|
|
24
27
|
const TICK_MS = 250;
|
|
25
28
|
const SESSION_TTL_MS = 1440 * 60 * 1e3;
|
|
26
29
|
const DISCONNECT_AFTER_MS = 45e3;
|
|
30
|
+
/** 没有实时通道时,HTTP 请求也算「人在」:这么久没动静才标成离线。 */
|
|
31
|
+
const PRESENCE_TTL_MS = 2e4;
|
|
27
32
|
const LOBBY_KICK_IDLE_MS = 600 * 1e3;
|
|
28
33
|
const CHAT_COOLDOWN_MS = 1200;
|
|
29
34
|
const BUY_IN_BB = 100;
|
|
@@ -66,6 +71,7 @@ const Config = z.object({
|
|
|
66
71
|
turnTimeoutSec: z.number().default(30).min(15).max(90),
|
|
67
72
|
autoAdvanceSec: z.number().default(6).min(2).max(30),
|
|
68
73
|
allowSpectators: z.boolean().default(true),
|
|
74
|
+
maxSpectators: z.number().default(DEFAULT_MAX_SPECTATORS).min(0).max(SPECTATORS_LIMIT),
|
|
69
75
|
chatEnabled: z.boolean().default(true),
|
|
70
76
|
maxRooms: z.number().default(MAX_ROOMS).min(1).max(64),
|
|
71
77
|
hostIdleDissolveMin: z.number().default(10).min(1).max(120),
|
|
@@ -90,6 +96,7 @@ function resolveConfig(config) {
|
|
|
90
96
|
turnTimeoutMs: turnTimeoutSec * 1e3,
|
|
91
97
|
autoAdvanceMs: autoAdvanceSec * 1e3,
|
|
92
98
|
allowSpectators: config.allowSpectators !== false,
|
|
99
|
+
maxSpectators: clamp(Math.trunc(config.maxSpectators ?? DEFAULT_MAX_SPECTATORS), 0, SPECTATORS_LIMIT),
|
|
93
100
|
chatEnabled: config.chatEnabled !== false,
|
|
94
101
|
maxRooms: clamp(Math.trunc(config.maxRooms ?? MAX_ROOMS), 1, 64),
|
|
95
102
|
hostIdleDissolveMs: clamp(config.hostIdleDissolveMin ?? 10, 1, 120) * 6e4,
|
|
@@ -1446,7 +1453,7 @@ function traceWs(line) {
|
|
|
1446
1453
|
function registerHoldemSocket(server, manager) {
|
|
1447
1454
|
const wss = new WebSocketServer({
|
|
1448
1455
|
noServer: true,
|
|
1449
|
-
handleProtocols: (protocols) => protocols.has(SUBPROTOCOL) ? SUBPROTOCOL :
|
|
1456
|
+
handleProtocols: (protocols) => protocols.has(SUBPROTOCOL) ? SUBPROTOCOL : ""
|
|
1450
1457
|
});
|
|
1451
1458
|
const sockets = /* @__PURE__ */ new Map();
|
|
1452
1459
|
const off = manager.onEvent((playerId, event) => {
|
|
@@ -3086,6 +3093,7 @@ var RoomManager = class {
|
|
|
3086
3093
|
turnTimeoutMs: this.config.turnTimeoutMs,
|
|
3087
3094
|
autoAdvanceMs: this.config.autoAdvanceMs,
|
|
3088
3095
|
allowSpectators: this.config.allowSpectators,
|
|
3096
|
+
maxSpectators: this.config.maxSpectators,
|
|
3089
3097
|
chatEnabled: this.config.chatEnabled
|
|
3090
3098
|
},
|
|
3091
3099
|
players: /* @__PURE__ */ new Map(),
|
|
@@ -3168,8 +3176,8 @@ var RoomManager = class {
|
|
|
3168
3176
|
if (cookieSession) {
|
|
3169
3177
|
const player$1 = room.players.get(cookieSession.playerId);
|
|
3170
3178
|
if (player$1 && !player$1.leaving) {
|
|
3171
|
-
player$1.connected = true;
|
|
3172
3179
|
player$1.browserId = input.browserId ?? player$1.browserId;
|
|
3180
|
+
this.touch(room, player$1);
|
|
3173
3181
|
return {
|
|
3174
3182
|
playerId: player$1.playerId,
|
|
3175
3183
|
roomId: room.roomId,
|
|
@@ -3184,8 +3192,8 @@ var RoomManager = class {
|
|
|
3184
3192
|
const seatedCount = [...room.players.values()].filter((player$1) => player$1.seat !== null).length;
|
|
3185
3193
|
const canSit = wanted === "sit" && invite.sit && room.phase === "lobby" && seatedCount < MAX_SEATS;
|
|
3186
3194
|
if (!canSit) {
|
|
3187
|
-
if (!room.settings.allowSpectators) throw new CommandError("room-full", "房间已满且不允许观战");
|
|
3188
|
-
if ([...room.players.values()].filter((player$1) => player$1.seat === null).length >=
|
|
3195
|
+
if (!room.settings.allowSpectators || room.settings.maxSpectators <= 0) throw new CommandError("room-full", "房间已满且不允许观战");
|
|
3196
|
+
if ([...room.players.values()].filter((player$1) => player$1.seat === null).length >= room.settings.maxSpectators) throw new CommandError("room-full", "观战席已满");
|
|
3189
3197
|
if (wanted === "sit" && invite.sit === false) throw new CommandError("auth", "没有入座权限");
|
|
3190
3198
|
}
|
|
3191
3199
|
const seat = canSit ? this.firstFreeSeat(room) : null;
|
|
@@ -3260,7 +3268,8 @@ var RoomManager = class {
|
|
|
3260
3268
|
view(roomId, playerId) {
|
|
3261
3269
|
const room = this.roomOf(playerId);
|
|
3262
3270
|
if (!room || room.roomId !== roomId) throw new CommandError("auth", "不在这个房间里");
|
|
3263
|
-
|
|
3271
|
+
const player = room.players.get(playerId);
|
|
3272
|
+
if (player) this.touch(room, player);
|
|
3264
3273
|
return this.project(room, playerId);
|
|
3265
3274
|
}
|
|
3266
3275
|
command(roomId, playerId, command, baseSeq, actionId) {
|
|
@@ -3268,7 +3277,7 @@ var RoomManager = class {
|
|
|
3268
3277
|
if (!room || room.roomId !== roomId) throw new CommandError("auth", "不在这个房间里");
|
|
3269
3278
|
const player = room.players.get(playerId);
|
|
3270
3279
|
if (!player || player.leaving) throw new CommandError("auth", "不在这个房间里");
|
|
3271
|
-
|
|
3280
|
+
this.touch(room, player);
|
|
3272
3281
|
if (command.type === "ping") return {
|
|
3273
3282
|
seq: room.seq,
|
|
3274
3283
|
view: this.project(room, playerId)
|
|
@@ -3411,13 +3420,32 @@ var RoomManager = class {
|
|
|
3411
3420
|
roomId: entry.roomId
|
|
3412
3421
|
};
|
|
3413
3422
|
}
|
|
3423
|
+
/**
|
|
3424
|
+
* 实时通道打开/关闭。
|
|
3425
|
+
*
|
|
3426
|
+
* 关掉 socket 不等于立刻离线:后台轮询(HTTP)还活着的话人还在。真正判离线交给
|
|
3427
|
+
* `tick` 按 `PRESENCE_TTL_MS` 扫一遍,两边都没有动静才标成离线。
|
|
3428
|
+
*/
|
|
3414
3429
|
markConnected(playerId, roomId, connected) {
|
|
3415
3430
|
const room = this.rooms.get(roomId);
|
|
3416
3431
|
const player = room?.players.get(playerId);
|
|
3417
3432
|
if (!room || !player) return;
|
|
3418
|
-
|
|
3419
|
-
if (player.
|
|
3420
|
-
|
|
3433
|
+
player.socketOpen = connected;
|
|
3434
|
+
if (connected) player.lastSeenAt = this.now();
|
|
3435
|
+
this.refreshPresence(room, player);
|
|
3436
|
+
}
|
|
3437
|
+
/** HTTP 请求也算心跳:轮询模式下不能把自己和别人都显示成离线。 */
|
|
3438
|
+
touch(room, player) {
|
|
3439
|
+
if (player.leaving) return;
|
|
3440
|
+
player.lastSeenAt = this.now();
|
|
3441
|
+
if (room.hostPlayerId === player.playerId) room.hostLastSeen = player.lastSeenAt;
|
|
3442
|
+
this.refreshPresence(room, player);
|
|
3443
|
+
}
|
|
3444
|
+
/** 有实时通道,或者最近有 HTTP 动静,就算在线。 */
|
|
3445
|
+
refreshPresence(room, player) {
|
|
3446
|
+
const online = !player.leaving && (player.socketOpen || this.now() - player.lastSeenAt < PRESENCE_TTL_MS);
|
|
3447
|
+
if (player.connected === online) return;
|
|
3448
|
+
player.connected = online;
|
|
3421
3449
|
this.publish(room);
|
|
3422
3450
|
}
|
|
3423
3451
|
health() {
|
|
@@ -3576,6 +3604,7 @@ var RoomManager = class {
|
|
|
3576
3604
|
if (room.phase === "hand" && room.hand) this.tickHand(room, now);
|
|
3577
3605
|
if (room.phase === "handOver" && room.handOverAt !== null && now >= room.handOverAt) this.advance(room, now);
|
|
3578
3606
|
for (const player of [...room.players.values()]) if (player.leaving && room.phase !== "hand") this.removePlayer(room, player.playerId);
|
|
3607
|
+
else this.refreshPresence(room, player);
|
|
3579
3608
|
}
|
|
3580
3609
|
this.cleanupSessions(now);
|
|
3581
3610
|
}
|
|
@@ -3726,6 +3755,8 @@ var RoomManager = class {
|
|
|
3726
3755
|
seat,
|
|
3727
3756
|
ready: false,
|
|
3728
3757
|
connected: true,
|
|
3758
|
+
socketOpen: false,
|
|
3759
|
+
lastSeenAt: this.now(),
|
|
3729
3760
|
sittingOut: false,
|
|
3730
3761
|
autoFold: false,
|
|
3731
3762
|
timeoutStreak: 0,
|