@zhin.js/plugin-tic-tac-toe 1.0.10 → 1.0.13

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.
@@ -6,11 +6,11 @@ import { resolveGameServices } from "../../lib/runtime-store.js";
6
6
  export default defineCommand({
7
7
  description: 'Tic Tac Toe',
8
8
  params: { action: { type: 'string', default: '' } },
9
- async execute({ params, input, use, owner }) {
9
+ async execute({ params, input, use }) {
10
10
  const action = normalizeTttAction(String(params.action ?? ''));
11
11
  if (!action || action === 'help')
12
12
  return TTT_HELP;
13
- const services = resolveGameServices({ use, owner });
13
+ const services = resolveGameServices({ use });
14
14
  const message = messageFromCommandInput(input);
15
15
  return runTttCommand(services, message, action);
16
16
  },
@@ -6,10 +6,10 @@ import { resolveGameServices } from '../../src/runtime-store.js';
6
6
  export default defineCommand({
7
7
  description: 'Tic Tac Toe',
8
8
  params: { action: { type: 'string', default: '' } },
9
- async execute({ params, input, use, owner }) {
9
+ async execute({ params, input, use }) {
10
10
  const action = normalizeTttAction(String(params.action ?? ''));
11
11
  if (!action || action === 'help') return TTT_HELP;
12
- const services = resolveGameServices({ use, owner });
12
+ const services = resolveGameServices({ use });
13
13
  const message = messageFromCommandInput(input);
14
14
  return runTttCommand(services, message, action);
15
15
  },
@@ -1,4 +1,4 @@
1
- import type { SendContent } from '@zhin.js/core';
1
+ import type { SendContent } from '@zhin.js/core/runtime';
2
2
  import { channelKey } from '@zhin.js/game-kit';
3
3
  import { type Board, type Cell } from './engine.js';
4
4
  /** 游戏前缀(用于 payload) */
package/lib/models.d.ts CHANGED
@@ -18,6 +18,10 @@ declare module '@zhin.js/core' {
18
18
  status: TttSessionStatus;
19
19
  winner: number;
20
20
  move_count: number;
21
+ /** 乐观锁版本(VersionedSessionService 契约) */
22
+ revision: number;
23
+ /** JSON 数组:已处理 actionId 历史(幂等去重) */
24
+ processed_actions: string;
21
25
  updated_at: number;
22
26
  created_at: number;
23
27
  };
package/lib/models.js CHANGED
@@ -15,6 +15,8 @@ export function defineHostTables(db) {
15
15
  status: { type: 'text', default: 'active' },
16
16
  winner: { type: 'integer', default: 0 },
17
17
  move_count: { type: 'integer', default: 0 },
18
+ revision: { type: 'integer', default: 0 },
19
+ processed_actions: { type: 'text', default: '[]' },
18
20
  updated_at: { type: 'integer', default: 0 },
19
21
  created_at: { type: 'integer', default: 0 },
20
22
  });
package/lib/models.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
+ import { type Token } from '@zhin.js/plugin-runtime';
1
2
  import type { SessionServices } from './session-service.js';
2
3
  /** Owner-scoped service consumed by discovered commands and middleware. */
3
- export declare const gameServicesToken: import("@zhin.js/plugin-runtime").Token<SessionServices>;
4
+ export declare const gameServicesToken: Token<SessionServices>;
4
5
  export declare function resolveGameServices(context: {
5
- use<T>(token: typeof gameServicesToken): T;
6
+ use<T>(token: Token<T>): T;
6
7
  }): SessionServices;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-store.js","sourceRoot":"","sources":["../src/runtime-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAkB,gCAAgC,CAAC,CAAC;AAEhG,MAAM,UAAU,mBAAmB,CAAC,OAAuD;IACzF,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAoB,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"runtime-store.js","sourceRoot":"","sources":["../src/runtime-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAc,MAAM,yBAAyB,CAAC;AAGlE,2EAA2E;AAC3E,MAAM,CAAC,MAAM,iBAAiB,GAAG,WAAW,CAAkB,gCAAgC,CAAC,CAAC;AAEhG,MAAM,UAAU,mBAAmB,CAAC,OAAuC;IACzE,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;AACxC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import type { Database, Models } from '@zhin.js/core';
2
- import { BaseSessionService, type GameMessageLike } from '@zhin.js/game-kit';
3
- import type { TttSessionRow } from './models.js';
2
+ import { VersionedSessionService, type GameMessageLike, type SessionMutationResult } from '@zhin.js/game-kit';
3
+ import type { TttSessionRow, TttSessionStatus } from './models.js';
4
4
  /** 井字棋服务使用的数据库实例(Models 经 models.ts 模块增强) */
5
5
  export type TttDatabase = Database<unknown, Models, string>;
6
6
  export type TttPlayerRef = {
@@ -10,6 +10,7 @@ export type TttPlayerRef = {
10
10
  /** 排队行过期时间(超时未匹配的排队视为失效) */
11
11
  export declare const QUEUE_TTL_MS: number;
12
12
  export declare class QueueService {
13
+ #private;
13
14
  private readonly db;
14
15
  constructor(db: TttDatabase);
15
16
  /** 清掉频道内过期排队行(ttt_queue 无 TTL,靠 join 时顺带清理) */
@@ -24,7 +25,7 @@ export declare class QueueService {
24
25
  count(channel: string): Promise<number>;
25
26
  }
26
27
  export declare function sessionId(): string;
27
- export declare class SessionService extends BaseSessionService<TttSessionRow> {
28
+ export declare class SessionService extends VersionedSessionService<TttSessionRow> {
28
29
  private readonly db;
29
30
  constructor(db: TttDatabase);
30
31
  createSession(input: {
@@ -35,6 +36,22 @@ export declare class SessionService extends BaseSessionService<TttSessionRow> {
35
36
  playerOName?: string;
36
37
  boardJson: string;
37
38
  }): Promise<TttSessionRow>;
39
+ /**
40
+ * 落子写路径:乐观锁(expectedRevision)+ actionId 幂等 + 进程内按会话串行
41
+ * (VersionedSessionService.mutateSession)。仅 applied 时补记 ttt_moves 流水
42
+ * —— 提前插入会让冲突/重试产生孤儿行。
43
+ */
44
+ performMove(input: {
45
+ readonly sessionId: string;
46
+ readonly playerId: string;
47
+ readonly cell: number;
48
+ readonly expectedRevision: number;
49
+ readonly boardJson: string;
50
+ readonly moveCount: number;
51
+ readonly nextTurn: number;
52
+ readonly status: TttSessionStatus;
53
+ readonly winner: number;
54
+ }): Promise<SessionMutationResult<TttSessionRow>>;
38
55
  recordMove(sessionId: string, playerId: string, cell: number, moveIndex: number): Promise<void>;
39
56
  addSpectator(sessionId: string, userId: string): Promise<void>;
40
57
  listSpectators(sessionId: string): Promise<string[]>;
@@ -1,4 +1,4 @@
1
- import { BaseSessionService, channelKey, generateCompactId, } from '@zhin.js/game-kit';
1
+ import { SessionActionGate, VersionedSessionService, channelKey, generateCompactId, } from '@zhin.js/game-kit';
2
2
  import { BOT_ID } from './player-label.js';
3
3
  /** 排队行过期时间(超时未匹配的排队视为失效) */
4
4
  export const QUEUE_TTL_MS = 10 * 60 * 1000;
@@ -11,6 +11,8 @@ function getModel(db, name) {
11
11
  }
12
12
  export class QueueService {
13
13
  db;
14
+ /** 同频道匹配串行化:并发 join→tryMatch 不得读到同一对玩家双开局。 */
15
+ #matchGate = new SessionActionGate();
14
16
  constructor(db) {
15
17
  this.db = db;
16
18
  }
@@ -53,22 +55,26 @@ export class QueueService {
53
55
  return rows.map((r) => r.user_id);
54
56
  }
55
57
  async tryMatch(channel) {
56
- const q = getModel(this.db, 'ttt_queue');
57
- const rows = await q.findAll({ channel_key: channel });
58
- rows.sort((a, b) => a.joined_at - b.joined_at);
59
- if (rows.length < 2)
60
- return null;
61
- // 只删除匹配到的两人,保留队列中其余的等待者
62
- await q.deleteWhere({ channel_key: channel, user_id: rows[0].user_id });
63
- await q.deleteWhere({ channel_key: channel, user_id: rows[1].user_id });
64
- const toRef = (userId, userName) => ({
65
- id: userId,
66
- displayName: userName.trim() || userId,
58
+ // 读-删-读非原子:两个并发调用者可能拿到同一对玩家。按频道串行后,
59
+ // 第二个调用者读到的是匹配后的队列(前两名已被删除),自然落空。
60
+ return this.#matchGate.run(`ttt-queue:${channel}`, async () => {
61
+ const q = getModel(this.db, 'ttt_queue');
62
+ const rows = await q.findAll({ channel_key: channel });
63
+ rows.sort((a, b) => a.joined_at - b.joined_at);
64
+ if (rows.length < 2)
65
+ return null;
66
+ // 只删除匹配到的两人,保留队列中其余的等待者
67
+ await q.deleteWhere({ channel_key: channel, user_id: rows[0].user_id });
68
+ await q.deleteWhere({ channel_key: channel, user_id: rows[1].user_id });
69
+ const toRef = (userId, userName) => ({
70
+ id: userId,
71
+ displayName: userName.trim() || userId,
72
+ });
73
+ return [
74
+ toRef(rows[0].user_id, rows[0].user_name),
75
+ toRef(rows[1].user_id, rows[1].user_name),
76
+ ];
67
77
  });
68
- return [
69
- toRef(rows[0].user_id, rows[0].user_name),
70
- toRef(rows[1].user_id, rows[1].user_name),
71
- ];
72
78
  }
73
79
  async count(channel) {
74
80
  const rows = await getModel(this.db, 'ttt_queue').findAll({ channel_key: channel });
@@ -78,7 +84,7 @@ export class QueueService {
78
84
  export function sessionId() {
79
85
  return generateCompactId('s');
80
86
  }
81
- export class SessionService extends BaseSessionService {
87
+ export class SessionService extends VersionedSessionService {
82
88
  db;
83
89
  constructor(db) {
84
90
  super(db, {
@@ -137,11 +143,35 @@ export class SessionService extends BaseSessionService {
137
143
  status: 'active',
138
144
  winner: 0,
139
145
  move_count: 0,
146
+ revision: 0,
147
+ processed_actions: '[]',
140
148
  updated_at: now,
141
149
  created_at: now,
142
150
  };
143
151
  return this.createRow(row);
144
152
  }
153
+ /**
154
+ * 落子写路径:乐观锁(expectedRevision)+ actionId 幂等 + 进程内按会话串行
155
+ * (VersionedSessionService.mutateSession)。仅 applied 时补记 ttt_moves 流水
156
+ * —— 提前插入会让冲突/重试产生孤儿行。
157
+ */
158
+ async performMove(input) {
159
+ const result = await this.mutateSession(input.sessionId, {
160
+ actionId: `${input.sessionId}:${input.expectedRevision}:${input.playerId}:${input.cell}`,
161
+ expectedRevision: input.expectedRevision,
162
+ apply: {
163
+ board: input.boardJson,
164
+ move_count: input.moveCount,
165
+ turn: input.nextTurn,
166
+ status: input.status,
167
+ winner: input.winner,
168
+ },
169
+ });
170
+ if (result.kind === 'applied') {
171
+ await this.recordMove(input.sessionId, input.playerId, input.cell, input.moveCount);
172
+ }
173
+ return result;
174
+ }
145
175
  async recordMove(sessionId, playerId, cell, moveIndex) {
146
176
  await getModel(this.db, 'ttt_moves').create({
147
177
  session_id: sessionId,
@@ -1 +1 @@
1
- {"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-service.js","sourceRoot":"","sources":["../src/session-service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/plugin-tic-tac-toe",
3
- "version": "1.0.10",
3
+ "version": "1.0.13",
4
4
  "description": "Zhin.js 跨平台井字棋 (Plugin Runtime)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -24,11 +24,11 @@
24
24
  ],
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
- "@zhin.js/command": "1.0.7",
28
- "@zhin.js/core": "1.5.2",
29
- "@zhin.js/game-kit": "3.0.2",
30
- "@zhin.js/middleware": "1.0.6",
31
- "@zhin.js/plugin-runtime": "1.1.3"
27
+ "@zhin.js/command": "1.0.10",
28
+ "@zhin.js/core": "1.5.5",
29
+ "@zhin.js/game-kit": "3.0.5",
30
+ "@zhin.js/middleware": "1.0.8",
31
+ "@zhin.js/plugin-runtime": "1.1.5"
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "^6.0.3",
package/plugin.js CHANGED
@@ -61,7 +61,7 @@ export default defineGamePlugin({
61
61
  }, session, status, terminal));
62
62
  await Promise.all(spectators.map((userId) => outbound.send({
63
63
  adapter: session.adapter,
64
- endpointId: session.endpoint,
64
+ endpointKey: session.endpoint,
65
65
  conversation: { kind: 'private', id: userId },
66
66
  content,
67
67
  })));
package/src/board-view.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SendContent } from '@zhin.js/core';
1
+ import type { SendContent } from '@zhin.js/core/runtime';
2
2
  import {
3
3
  buildGridKeyboard,
4
4
  buildGridFallbackMap,
package/src/models.ts CHANGED
@@ -20,6 +20,10 @@ declare module '@zhin.js/core' {
20
20
  status: TttSessionStatus;
21
21
  winner: number;
22
22
  move_count: number;
23
+ /** 乐观锁版本(VersionedSessionService 契约) */
24
+ revision: number;
25
+ /** JSON 数组:已处理 actionId 历史(幂等去重) */
26
+ processed_actions: string;
23
27
  updated_at: number;
24
28
  created_at: number;
25
29
  };
@@ -75,6 +79,8 @@ export function defineHostTables(
75
79
  status: { type: 'text', default: 'active' },
76
80
  winner: { type: 'integer', default: 0 },
77
81
  move_count: { type: 'integer', default: 0 },
82
+ revision: { type: 'integer', default: 0 },
83
+ processed_actions: { type: 'text', default: '[]' },
78
84
  updated_at: { type: 'integer', default: 0 },
79
85
  created_at: { type: 'integer', default: 0 },
80
86
  });
@@ -1,9 +1,9 @@
1
- import { createToken } from '@zhin.js/plugin-runtime';
1
+ import { createToken, type Token } from '@zhin.js/plugin-runtime';
2
2
  import type { SessionServices } from './session-service.js';
3
3
 
4
4
  /** Owner-scoped service consumed by discovered commands and middleware. */
5
5
  export const gameServicesToken = createToken<SessionServices>('zhin.game.tic-tac-toe.services');
6
6
 
7
- export function resolveGameServices(context: { use<T>(token: typeof gameServicesToken): T }): SessionServices {
8
- return context.use(gameServicesToken) as SessionServices;
7
+ export function resolveGameServices(context: { use<T>(token: Token<T>): T }): SessionServices {
8
+ return context.use(gameServicesToken);
9
9
  }
@@ -1,12 +1,14 @@
1
1
  import type { Database, Models, RelatedModel } from '@zhin.js/core';
2
2
  import {
3
- BaseSessionService,
3
+ SessionActionGate,
4
+ VersionedSessionService,
4
5
  channelKey,
5
6
  generateCompactId,
6
7
  type GameMessageLike,
7
8
  type GameSessionDatabase,
9
+ type SessionMutationResult,
8
10
  } from '@zhin.js/game-kit';
9
- import type { TttModelName, TttSessionRow } from './models.js';
11
+ import type { TttModelName, TttSessionRow, TttSessionStatus } from './models.js';
10
12
  import { BOT_ID } from './player-label.js';
11
13
 
12
14
  /** 井字棋服务使用的数据库实例(Models 经 models.ts 模块增强) */
@@ -28,6 +30,9 @@ function getModel<K extends TttModelName>(db: TttDatabase, name: K): TttModel<K>
28
30
  }
29
31
 
30
32
  export class QueueService {
33
+ /** 同频道匹配串行化:并发 join→tryMatch 不得读到同一对玩家双开局。 */
34
+ readonly #matchGate = new SessionActionGate();
35
+
31
36
  constructor(private readonly db: TttDatabase) {}
32
37
 
33
38
  /** 清掉频道内过期排队行(ttt_queue 无 TTL,靠 join 时顺带清理) */
@@ -76,21 +81,25 @@ export class QueueService {
76
81
  }
77
82
 
78
83
  async tryMatch(channel: string): Promise<[TttPlayerRef, TttPlayerRef] | null> {
79
- const q = getModel(this.db, 'ttt_queue');
80
- const rows = await q.findAll({ channel_key: channel });
81
- rows.sort((a, b) => a.joined_at - b.joined_at);
82
- if (rows.length < 2) return null;
83
- // 只删除匹配到的两人,保留队列中其余的等待者
84
- await q.deleteWhere({ channel_key: channel, user_id: rows[0]!.user_id });
85
- await q.deleteWhere({ channel_key: channel, user_id: rows[1]!.user_id });
86
- const toRef = (userId: string, userName: string) => ({
87
- id: userId,
88
- displayName: userName.trim() || userId,
84
+ // 读-删-读非原子:两个并发调用者可能拿到同一对玩家。按频道串行后,
85
+ // 第二个调用者读到的是匹配后的队列(前两名已被删除),自然落空。
86
+ return this.#matchGate.run(`ttt-queue:${channel}`, async () => {
87
+ const q = getModel(this.db, 'ttt_queue');
88
+ const rows = await q.findAll({ channel_key: channel });
89
+ rows.sort((a, b) => a.joined_at - b.joined_at);
90
+ if (rows.length < 2) return null;
91
+ // 只删除匹配到的两人,保留队列中其余的等待者
92
+ await q.deleteWhere({ channel_key: channel, user_id: rows[0]!.user_id });
93
+ await q.deleteWhere({ channel_key: channel, user_id: rows[1]!.user_id });
94
+ const toRef = (userId: string, userName: string) => ({
95
+ id: userId,
96
+ displayName: userName.trim() || userId,
97
+ });
98
+ return [
99
+ toRef(rows[0]!.user_id, rows[0]!.user_name),
100
+ toRef(rows[1]!.user_id, rows[1]!.user_name),
101
+ ];
89
102
  });
90
- return [
91
- toRef(rows[0]!.user_id, rows[0]!.user_name),
92
- toRef(rows[1]!.user_id, rows[1]!.user_name),
93
- ];
94
103
  }
95
104
 
96
105
  async count(channel: string): Promise<number> {
@@ -103,7 +112,7 @@ export function sessionId(): string {
103
112
  return generateCompactId('s');
104
113
  }
105
114
 
106
- export class SessionService extends BaseSessionService<TttSessionRow> {
115
+ export class SessionService extends VersionedSessionService<TttSessionRow> {
107
116
  constructor(private readonly db: TttDatabase) {
108
117
  super(db as unknown as GameSessionDatabase<TttSessionRow>, {
109
118
  gameId: 'ttt',
@@ -167,12 +176,47 @@ export class SessionService extends BaseSessionService<TttSessionRow> {
167
176
  status: 'active',
168
177
  winner: 0,
169
178
  move_count: 0,
179
+ revision: 0,
180
+ processed_actions: '[]',
170
181
  updated_at: now,
171
182
  created_at: now,
172
183
  };
173
184
  return this.createRow(row);
174
185
  }
175
186
 
187
+ /**
188
+ * 落子写路径:乐观锁(expectedRevision)+ actionId 幂等 + 进程内按会话串行
189
+ * (VersionedSessionService.mutateSession)。仅 applied 时补记 ttt_moves 流水
190
+ * —— 提前插入会让冲突/重试产生孤儿行。
191
+ */
192
+ async performMove(input: {
193
+ readonly sessionId: string;
194
+ readonly playerId: string;
195
+ readonly cell: number;
196
+ readonly expectedRevision: number;
197
+ readonly boardJson: string;
198
+ readonly moveCount: number;
199
+ readonly nextTurn: number;
200
+ readonly status: TttSessionStatus;
201
+ readonly winner: number;
202
+ }): Promise<SessionMutationResult<TttSessionRow>> {
203
+ const result = await this.mutateSession(input.sessionId, {
204
+ actionId: `${input.sessionId}:${input.expectedRevision}:${input.playerId}:${input.cell}`,
205
+ expectedRevision: input.expectedRevision,
206
+ apply: {
207
+ board: input.boardJson,
208
+ move_count: input.moveCount,
209
+ turn: input.nextTurn,
210
+ status: input.status,
211
+ winner: input.winner,
212
+ },
213
+ });
214
+ if (result.kind === 'applied') {
215
+ await this.recordMove(input.sessionId, input.playerId, input.cell, input.moveCount);
216
+ }
217
+ return result;
218
+ }
219
+
176
220
  async recordMove(sessionId: string, playerId: string, cell: number, moveIndex: number): Promise<void> {
177
221
  await getModel(this.db, 'ttt_moves').create({
178
222
  session_id: sessionId,