@zhin.js/plugin-text-adventure 1.0.3 → 1.0.5

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.
@@ -0,0 +1,16 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from '@zhin.js/command';
3
+ import { messageFromCommandInput, normalizeAdvAction } from '@zhin.js/game-kit';
4
+ import { ADV_HELP, runAdvCommand } from "../../lib/adv-command.js";
5
+ import { resolveGameServices } from "../../lib/runtime-store.js";
6
+ export default defineCommand({
7
+ description: 'Text Adventure',
8
+ async execute({ params, input, use, owner }) {
9
+ const action = normalizeAdvAction(String(params.action ?? ''));
10
+ if (!action || action === 'help')
11
+ return ADV_HELP;
12
+ const services = resolveGameServices({ use, owner });
13
+ const message = messageFromCommandInput(input);
14
+ return runAdvCommand(services, message, action);
15
+ },
16
+ });
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from '@zhin.js/command';
2
2
  import { messageFromCommandInput, normalizeAdvAction } from '@zhin.js/game-kit';
3
- import { ADV_HELP, runAdvCommandText } from '../../src/adv-command.js';
3
+ import { ADV_HELP, runAdvCommand } from '../../src/adv-command.js';
4
4
  import { resolveGameServices } from '../../src/runtime-store.js';
5
5
 
6
6
  export default defineCommand({
@@ -10,6 +10,6 @@ export default defineCommand({
10
10
  if (!action || action === 'help') return ADV_HELP;
11
11
  const services = resolveGameServices({ use, owner });
12
12
  const message = messageFromCommandInput(input);
13
- return runAdvCommandText(services, message, action);
13
+ return runAdvCommand(services, message, action);
14
14
  },
15
15
  });
@@ -1,6 +1,4 @@
1
- import type { Message, Plugin } from '@zhin.js/core';
1
+ import { type GameMessageLike, type GameReply } from '@zhin.js/game-kit';
2
2
  import type { GameServices } from './session-service.js';
3
3
  export declare const ADV_HELP: string;
4
- export declare function runAdvCommand(plugin: Plugin | null, services: GameServices, message: Message<any>, action: string): Promise<string | undefined>;
5
- /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
6
- export declare function runAdvCommandText(services: GameServices, message: Message<any>, action: string): Promise<string>;
4
+ export declare function runAdvCommand(services: GameServices, message: GameMessageLike, action: string): Promise<GameReply>;
@@ -1,4 +1,4 @@
1
- import { channelKey } from '@zhin.js/game-kit';
1
+ import { channelKey, } from '@zhin.js/game-kit';
2
2
  import { continueAdventure, sessionSummary, startAdventure, } from './game-flow.js';
3
3
  import { formatAchievements, formatMapProgress, formatProgressCompact } from './profile-format.js';
4
4
  export const ADV_HELP = [
@@ -12,7 +12,7 @@ export const ADV_HELP = [
12
12
  '',
13
13
  '推进方式:点击选项按钮,或回复数字(1、2…)',
14
14
  ].join('\n');
15
- export async function runAdvCommand(plugin, services, message, action) {
15
+ export async function runAdvCommand(services, message, action) {
16
16
  const ch = channelKey(message);
17
17
  const userId = message.$sender.id;
18
18
  const userName = message.$sender.name?.trim() || userId;
@@ -30,10 +30,10 @@ export async function runAdvCommand(plugin, services, message, action) {
30
30
  return lines.join('\n');
31
31
  }
32
32
  if (action === 'start') {
33
- return startAdventure(plugin, services, message);
33
+ return (await startAdventure(services, message)) ?? '';
34
34
  }
35
35
  if (action === 'continue') {
36
- return continueAdventure(plugin, services, message);
36
+ return continueAdventure(services, message);
37
37
  }
38
38
  if (action === 'map') {
39
39
  const profile = await services.profiles.getOrCreate(userId, userName);
@@ -52,8 +52,4 @@ export async function runAdvCommand(plugin, services, message, action) {
52
52
  }
53
53
  return `未知子命令:${action}\n\n${ADV_HELP}`;
54
54
  }
55
- /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
56
- export async function runAdvCommandText(services, message, action) {
57
- return (await runAdvCommand(null, services, message, action)) ?? '';
58
- }
59
55
  //# sourceMappingURL=adv-command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adv-command.js","sourceRoot":"","sources":["../src/adv-command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adv-command.js","sourceRoot":"","sources":["../src/adv-command.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import type { Message, Plugin } from '@zhin.js/core';
1
+ import type { GameMessageLike, GameReply } from '@zhin.js/game-kit';
2
2
  import type { AdvProfileRow, AdvSessionRow } from './models.js';
3
3
  import type { GameServices } from './session-service.js';
4
- export declare function sendOrEditScene(plugin: Plugin | null, services: GameServices, message: Message<any>, session: AdvSessionRow, extraNarrative?: string, profile?: AdvProfileRow): Promise<string>;
5
- export declare function startAdventure(plugin: Plugin | null, services: GameServices, message: Message<any>): Promise<string | undefined>;
6
- export declare function handleChoice(plugin: Plugin | null, services: GameServices, message: Message<any>, sessionId: string, choiceId: string): Promise<string | null>;
7
- export declare function continueAdventure(plugin: Plugin | null, services: GameServices, message: Message<any>): Promise<string>;
4
+ export declare function sendOrEditScene(services: GameServices, message: GameMessageLike, session: AdvSessionRow, extraNarrative?: string, profile?: AdvProfileRow): Promise<GameReply>;
5
+ export declare function startAdventure(services: GameServices, message: GameMessageLike): Promise<GameReply | undefined>;
6
+ export declare function handleChoice(services: GameServices, message: GameMessageLike, sessionId: string, choiceId: string): Promise<GameReply | null>;
7
+ export declare function continueAdventure(services: GameServices, message: GameMessageLike): Promise<GameReply>;
8
8
  export declare function sessionSummary(session: AdvSessionRow): string;
package/lib/game-flow.js CHANGED
@@ -1,38 +1,16 @@
1
- import { plainTextFromSendContent } from '@zhin.js/game-kit';
2
1
  import { formatNewAchievements } from './profile-format.js';
3
2
  import { buildSceneInteractive } from './scene-view.js';
4
- import { applyChoiceResult, getScene, resolveChoice, stateFromSession, } from './story.js';
5
- export async function sendOrEditScene(plugin, services, message, session, extraNarrative = '', profile) {
3
+ import { applyChoiceResult, getScene, resolveChoice, stateFromSession, visibleChoices, } from './story.js';
4
+ export async function sendOrEditScene(services, message, session, extraNarrative = '', profile) {
6
5
  const prof = profile
7
6
  ?? await services.profiles.getOrCreate(session.player_id, session.player_name);
8
7
  const content = buildSceneInteractive(session, extraNarrative, prof, message.$channel.type);
9
8
  if (!content) {
10
9
  return '场景数据异常,请 adv quit 后重新开始。';
11
10
  }
12
- if (!plugin)
13
- return plainTextFromSendContent(content);
14
- const adapter = plugin.root.inject(message.$adapter);
15
- if (session.board_message_id) {
16
- const msgId = await adapter.editMessage({
17
- messageId: session.board_message_id,
18
- context: String(message.$adapter),
19
- endpoint: message.$endpoint,
20
- id: message.$channel.id,
21
- type: message.$channel.type,
22
- content,
23
- });
24
- if (msgId !== session.board_message_id) {
25
- await services.sessions.updateSession(session.id, { board_message_id: msgId });
26
- }
27
- return msgId;
28
- }
29
- const msgId = await message.$reply?.(content);
30
- if (msgId) {
31
- await services.sessions.updateSession(session.id, { board_message_id: msgId });
32
- }
33
- return msgId ?? '';
11
+ return content;
34
12
  }
35
- export async function startAdventure(plugin, services, message) {
13
+ export async function startAdventure(services, message) {
36
14
  const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
37
15
  const active = await services.sessions.getActiveByChannel(ch);
38
16
  if (active) {
@@ -44,12 +22,12 @@ export async function startAdventure(plugin, services, message) {
44
22
  await services.profiles.onRunStarted(message.$sender.id, message.$sender.name?.trim() || message.$sender.id);
45
23
  const session = await services.sessions.createSession(message);
46
24
  await services.profiles.onStep(message.$sender.id, session.player_name, 'start', []);
47
- const text = await sendOrEditScene(plugin, services, message, session);
48
- return plugin ? undefined : text;
25
+ return sendOrEditScene(services, message, session);
49
26
  }
50
- export async function handleChoice(plugin, services, message, sessionId, choiceId) {
27
+ export async function handleChoice(services, message, sessionId, choiceId) {
51
28
  const session = await services.sessions.getById(sessionId);
52
- if (!session || session.status !== 'active') {
29
+ // 踏进 terminal 场景时 status 必为 completed,终局需放行「再玩一次」(restart)
30
+ if (!session || (session.status !== 'active' && !(session.status === 'completed' && choiceId === 'restart'))) {
53
31
  return '冒险不存在或已结束。';
54
32
  }
55
33
  if (session.player_id !== message.$sender.id) {
@@ -71,13 +49,15 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
71
49
  ending_id: '',
72
50
  status: 'active',
73
51
  step_count: 0,
74
- board_message_id: '',
75
52
  });
76
53
  const updated = (await services.sessions.getById(session.id));
54
+ await services.profiles.onRunStarted(updated.player_id, updated.player_name);
77
55
  await services.profiles.onStep(updated.player_id, updated.player_name, 'start', []);
78
- const text = await sendOrEditScene(plugin, services, message, updated);
79
- // text-only 模式(plugin===null)下 sendOrEditScene 的唯一输出就是返回文本
80
- return plugin ? null : text;
56
+ return sendOrEditScene(services, message, updated);
57
+ }
58
+ // 服务端校验:选项必须当前可见(requires 门槛不能只靠视图层过滤,payload 可绕过)
59
+ if (!visibleChoices(scene, state).some((c) => c.id === choiceId)) {
60
+ return '该选项不可用。';
81
61
  }
82
62
  const result = resolveChoice(state, choiceId);
83
63
  if (!result)
@@ -108,18 +88,13 @@ export async function handleChoice(plugin, services, message, sessionId, choiceI
108
88
  }
109
89
  const updated = (await services.sessions.getById(session.id));
110
90
  const extra = formatNewAchievements(newAchievements);
111
- const text = await sendOrEditScene(plugin, services, message, updated, extra);
112
- // text-only 模式(plugin===null)下 sendOrEditScene 的唯一输出就是返回文本
113
- return plugin ? null : text;
91
+ return sendOrEditScene(services, message, updated, extra);
114
92
  }
115
- export async function continueAdventure(plugin, services, message) {
93
+ export async function continueAdventure(services, message) {
116
94
  const session = await services.sessions.getActiveForUser(`${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`, message.$sender.id);
117
95
  if (!session)
118
96
  return '你没有进行中的冒险,发送 adv start 开始。';
119
- const text = await sendOrEditScene(plugin, services, message, session);
120
- if (!plugin)
121
- return text;
122
- return '已刷新当前场景。';
97
+ return sendOrEditScene(services, message, session);
123
98
  }
124
99
  export function sessionSummary(session) {
125
100
  const scene = getScene(session.scene_id);
@@ -1 +1 @@
1
- {"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"game-flow.js","sourceRoot":"","sources":["../src/game-flow.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export { ADV_HELP } from './adv-command.js';
2
2
  export { gameServicesToken, resolveGameServices } from './runtime-store.js';
3
- export { createInMemoryAdvDb, mountAdvMemoryServices } from './memory-db.js';
4
3
  /**
5
4
  * 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
6
5
  * 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
package/lib/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  export { ADV_HELP } from './adv-command.js';
2
2
  export { gameServicesToken, resolveGameServices } from './runtime-store.js';
3
- export { createInMemoryAdvDb, mountAdvMemoryServices } from './memory-db.js';
4
3
  /**
5
4
  * 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
6
5
  * 游戏大厅注册(registerRuntimeGame)、过期会话 cron(scheduleHostToken)、
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7E;;;;GAIG"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE5E;;;;GAIG"}
package/lib/models.d.ts CHANGED
@@ -17,7 +17,6 @@ declare module '@zhin.js/core' {
17
17
  flags: string;
18
18
  ending_id: string;
19
19
  status: AdvSessionStatus;
20
- board_message_id: string;
21
20
  step_count: number;
22
21
  updated_at: number;
23
22
  created_at: number;
package/lib/models.js CHANGED
@@ -14,7 +14,6 @@ export function defineHostTables(db) {
14
14
  flags: { type: 'text', default: '{}' },
15
15
  ending_id: { type: 'text', default: '' },
16
16
  status: { type: 'text', default: 'active' },
17
- board_message_id: { type: 'text', default: '' },
18
17
  step_count: { type: 'integer', default: 0 },
19
18
  updated_at: { type: 'integer', default: 0 },
20
19
  created_at: { type: 'integer', default: 0 },
package/lib/models.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAgDA,MAAM,UAAU,gBAAgB,CAC9B,EAA2E;IAE3E,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE;QACxB,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACnC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC3C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC/C,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC9C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC5C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE;QAC5C,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE;QACrC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1C,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACtC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACxC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC3C,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC/C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;KAC5C,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE;QACxB,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1C,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC/C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC5C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC7C,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC7C,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC/C,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC5C,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAChD,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;KAC5C,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AA+CA,MAAM,UAAU,gBAAgB,CAC9B,EAA2E;IAE3E,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE;QACxB,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACnC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC3C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC/C,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC9C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE;QAC5C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE;QAC5C,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE;QACrC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1C,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QACtC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACxC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;KAC5C,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE;QACxB,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC1C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1C,cAAc,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC/C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC5C,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAC7C,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC7C,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC/C,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC5C,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAChD,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;QAC3C,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;KAC5C,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,5 @@
1
- import type { Database, Message, Models } from '@zhin.js/core';
1
+ import type { Database, Models } from '@zhin.js/core';
2
+ import { BaseSessionService, type GameMessageLike } from '@zhin.js/game-kit';
2
3
  import type { AdvSessionRow } from './models.js';
3
4
  import { ProfileService } from './profile-service.js';
4
5
  export type AdvDatabase = Database<unknown, Models, string>;
@@ -6,15 +7,8 @@ export interface GameServices {
6
7
  sessions: SessionService;
7
8
  profiles: ProfileService;
8
9
  }
9
- export declare class SessionService {
10
- private readonly db;
10
+ export declare class SessionService extends BaseSessionService<AdvSessionRow> {
11
11
  constructor(db: AdvDatabase);
12
- getActiveByChannel(channel: string): Promise<AdvSessionRow | null>;
13
- getActiveForUser(channel: string, userId: string): Promise<AdvSessionRow | null>;
14
- getById(id: string): Promise<AdvSessionRow | null>;
15
- getActiveByBoardMessageId(messageId: string): Promise<AdvSessionRow | null>;
16
- createSession(message: Message<any>): Promise<AdvSessionRow>;
17
- updateSession(id: string, patch: Partial<AdvSessionRow>): Promise<void>;
18
- abortStale(idleMs: number): Promise<number>;
12
+ createSession(message: GameMessageLike): Promise<AdvSessionRow>;
19
13
  }
20
14
  export declare function createServices(db: AdvDatabase): GameServices;
@@ -1,4 +1,4 @@
1
- import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-kit';
1
+ import { BaseSessionService, channelKey, generateSessionId, } from '@zhin.js/game-kit';
2
2
  import { createProfileService } from './profile-service.js';
3
3
  function getModel(db, name) {
4
4
  const model = db.models.get(name);
@@ -6,36 +6,13 @@ function getModel(db, name) {
6
6
  throw new Error(`Model ${name} is not registered`);
7
7
  return model;
8
8
  }
9
- export class SessionService {
10
- db;
9
+ export class SessionService extends BaseSessionService {
11
10
  constructor(db) {
12
- this.db = db;
13
- }
14
- async getActiveByChannel(channel) {
15
- const rows = await getModel(this.db, 'adv_sessions').findAll({
16
- channel_key: channel,
17
- status: 'active',
11
+ super(db, {
12
+ gameId: 'adv',
13
+ table: 'adv_sessions',
14
+ userFields: ['player_id'],
18
15
  });
19
- return rows[0] ?? null;
20
- }
21
- async getActiveForUser(channel, userId) {
22
- const row = await this.getActiveByChannel(channel);
23
- if (!row || row.player_id !== userId)
24
- return null;
25
- return row;
26
- }
27
- async getById(id) {
28
- return getModel(this.db, 'adv_sessions').findOne({ id });
29
- }
30
- async getActiveByBoardMessageId(messageId) {
31
- if (!messageId)
32
- return null;
33
- const rows = await getModel(this.db, 'adv_sessions').findAll({});
34
- for (const row of rows) {
35
- if (boardMessageMatches(row.board_message_id ?? '', messageId))
36
- return row;
37
- }
38
- return null;
39
16
  }
40
17
  async createSession(message) {
41
18
  const now = Date.now();
@@ -55,29 +32,11 @@ export class SessionService {
55
32
  flags: '{}',
56
33
  ending_id: '',
57
34
  status: 'active',
58
- board_message_id: '',
59
35
  step_count: 0,
60
36
  updated_at: now,
61
37
  created_at: now,
62
38
  };
63
- await getModel(this.db, 'adv_sessions').create(row);
64
- return row;
65
- }
66
- async updateSession(id, patch) {
67
- await getModel(this.db, 'adv_sessions').updateWhere({ id }, { ...patch, updated_at: Date.now() });
68
- }
69
- async abortStale(idleMs) {
70
- const cutoff = Date.now() - idleMs;
71
- const sessions = getModel(this.db, 'adv_sessions');
72
- const rows = await sessions.findAll({ status: 'active' });
73
- let n = 0;
74
- for (const row of rows) {
75
- if (row.updated_at < cutoff) {
76
- await sessions.updateWhere({ id: row.id }, { status: 'aborted', updated_at: Date.now() });
77
- n++;
78
- }
79
- }
80
- return n;
39
+ return this.createRow(row);
81
40
  }
82
41
  }
83
42
  export function createServices(db) {
@@ -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":"AACA,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,iBAAiB,GAGlB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,oBAAoB,EAAkB,MAAM,sBAAsB,CAAC;AAK5E,SAAS,QAAQ,CAAyB,EAAe,EAAE,IAAO;IAChE,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,oBAAoB,CAAC,CAAC;IAC/D,OAAO,KAAoB,CAAC;AAC9B,CAAC;AAOD,MAAM,OAAO,cAAe,SAAQ,kBAAiC;IACnE,YAAY,EAAe;QACzB,KAAK,CAAC,EAAmD,EAAE;YACzD,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,cAAc;YACrB,UAAU,EAAE,CAAC,WAAW,CAAC;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAwB;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAkB;YACzB,EAAE;YACF,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjC,QAAQ,EAAE,OAAO,CAAC,SAAS;YAC3B,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI;YACnC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE;YAC/B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC;YAChC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;YAC7B,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC/D,QAAQ,EAAE,OAAO;YACjB,EAAE,EAAE,GAAG;YACP,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,IAAI;YACX,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,GAAG;YACf,UAAU,EAAE,GAAG;SAChB,CAAC;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,EAAe;IAC5C,OAAO;QACL,QAAQ,EAAE,IAAI,cAAc,CAAC,EAAE,CAAC;QAChC,QAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC;KACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineGameCommandAliasMiddleware, isAdvAction, messageFromCommandInput, normalizeAdvAction, } from '@zhin.js/game-kit';
3
+ import { ADV_HELP, runAdvCommand } from "../lib/adv-command.js";
4
+ import { resolveGameServices } from "../lib/runtime-store.js";
5
+ export default defineGameCommandAliasMiddleware({
6
+ aliases: ['冒险', 'adv', '秘境'],
7
+ async run(action, input, context) {
8
+ const normalized = normalizeAdvAction(String(action ?? ''));
9
+ if (normalized === 'help')
10
+ return ADV_HELP;
11
+ // action 无法识别:放行给后续中间件,避免劫持普通聊天
12
+ if (!isAdvAction(normalized))
13
+ return null;
14
+ const services = resolveGameServices(context);
15
+ const message = messageFromCommandInput(input);
16
+ return runAdvCommand(services, message, normalized);
17
+ },
18
+ });
@@ -1,18 +1,21 @@
1
1
  import {
2
2
  defineGameCommandAliasMiddleware,
3
+ isAdvAction,
3
4
  messageFromCommandInput,
4
5
  normalizeAdvAction,
5
6
  } from '@zhin.js/game-kit';
6
- import { ADV_HELP, runAdvCommandText } from '../src/adv-command.js';
7
+ import { ADV_HELP, runAdvCommand } from '../src/adv-command.js';
7
8
  import { resolveGameServices } from '../src/runtime-store.js';
8
9
 
9
10
  export default defineGameCommandAliasMiddleware({
10
11
  aliases: ['冒险', 'adv', '秘境'],
11
12
  async run(action, input, context) {
12
13
  const normalized = normalizeAdvAction(String(action ?? ''));
13
- if (!normalized || normalized === 'help') return ADV_HELP;
14
+ if (normalized === 'help') return ADV_HELP;
15
+ // action 无法识别:放行给后续中间件,避免劫持普通聊天
16
+ if (!isAdvAction(normalized)) return null;
14
17
  const services = resolveGameServices(context);
15
18
  const message = messageFromCommandInput(input);
16
- return runAdvCommandText(services, message, normalized);
19
+ return runAdvCommand(services, message, normalized);
17
20
  },
18
21
  });
@@ -0,0 +1,61 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineMiddleware } from '@zhin.js/middleware';
3
+ import { buildChoiceFallbackMap, channelKey, messageFromCommandInput, parseChoicePayload, resolveGameTextPayload, } from '@zhin.js/game-kit';
4
+ import { resolveGameServices } from "../lib/runtime-store.js";
5
+ import { handleChoice } from "../lib/game-flow.js";
6
+ import { ADV_PREFIX, getScene, stateFromSession, visibleChoices } from "../lib/story.js";
7
+ /**
8
+ * 文本入口:按钮 payload(`adv:session:choiceId`)与数字 fallback
9
+ * (按当前场景可见选项编号,对应视图提示用户回复数字选分支)。
10
+ */
11
+ export default defineMiddleware({
12
+ target: 'inbound',
13
+ async handle(context, next) {
14
+ const raw = context.input.content?.trim() ?? '';
15
+ if (!raw) {
16
+ await next();
17
+ return;
18
+ }
19
+ const services = resolveGameServices(context);
20
+ const message = messageFromCommandInput(context.input);
21
+ const ch = channelKey(message);
22
+ // 直接 payload(QQ 指令预填 / Sandbox action→text)
23
+ const payloadFromText = resolveGameTextPayload(raw);
24
+ if (payloadFromText?.startsWith(`${ADV_PREFIX}:`)) {
25
+ const parsed = parseChoicePayload(payloadFromText, ADV_PREFIX);
26
+ if (parsed) {
27
+ const session = await services.sessions.getById(parsed.sessionId);
28
+ if (session?.channel_key === ch) {
29
+ const reply = await handleChoice(services, message, parsed.sessionId, parsed.choiceId);
30
+ if (reply)
31
+ await context.input.$reply(reply);
32
+ return;
33
+ }
34
+ }
35
+ await next();
36
+ return;
37
+ }
38
+ const session = await services.sessions.getActiveForUser(ch, message.$sender.id);
39
+ if (!session) {
40
+ await next();
41
+ return;
42
+ }
43
+ const scene = getScene(session.scene_id);
44
+ if (!scene) {
45
+ await next();
46
+ return;
47
+ }
48
+ const state = stateFromSession(session);
49
+ const choices = visibleChoices(scene, state);
50
+ const map = buildChoiceFallbackMap(ADV_PREFIX, session.id, choices);
51
+ const payload = resolveGameTextPayload(raw, map);
52
+ const parsed = payload ? parseChoicePayload(payload, ADV_PREFIX) : null;
53
+ if (!parsed || parsed.sessionId !== session.id) {
54
+ await next();
55
+ return;
56
+ }
57
+ const reply = await handleChoice(services, message, session.id, parsed.choiceId);
58
+ if (reply)
59
+ await context.input.$reply(reply);
60
+ },
61
+ });
@@ -34,7 +34,7 @@ export default defineMiddleware<Message>({
34
34
  if (parsed) {
35
35
  const session = await services.sessions.getById(parsed.sessionId);
36
36
  if (session?.channel_key === ch) {
37
- const reply = await handleChoice(null, services, message, parsed.sessionId, parsed.choiceId);
37
+ const reply = await handleChoice(services, message, parsed.sessionId, parsed.choiceId);
38
38
  if (reply) await context.input.$reply(reply);
39
39
  return;
40
40
  }
@@ -64,7 +64,7 @@ export default defineMiddleware<Message>({
64
64
  return;
65
65
  }
66
66
 
67
- const reply = await handleChoice(null, services, message, session.id, parsed.choiceId);
67
+ const reply = await handleChoice(services, message, session.id, parsed.choiceId);
68
68
  if (reply) await context.input.$reply(reply);
69
69
  },
70
70
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/plugin-text-adventure",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Zhin.js 交互式文字冒险 (Plugin Runtime)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -14,7 +14,7 @@
14
14
  "./package.json": "./package.json"
15
15
  },
16
16
  "files": [
17
- "plugin.ts",
17
+ "plugin.js",
18
18
  "schema.json",
19
19
  "commands",
20
20
  "middlewares",
@@ -24,11 +24,11 @@
24
24
  ],
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
- "@zhin.js/command": "1.0.2",
28
- "@zhin.js/core": "1.4.0",
29
- "@zhin.js/game-kit": "2.0.0",
30
- "@zhin.js/middleware": "1.0.2",
31
- "@zhin.js/plugin-runtime": "1.1.0"
27
+ "@zhin.js/command": "1.0.4",
28
+ "@zhin.js/core": "1.4.2",
29
+ "@zhin.js/game-kit": "2.0.2",
30
+ "@zhin.js/middleware": "1.0.4",
31
+ "@zhin.js/plugin-runtime": "1.1.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "typescript": "^6.0.3",
@@ -49,7 +49,7 @@
49
49
  "zhin": {
50
50
  "protocol": 1,
51
51
  "type": "plugin",
52
- "entry": "./plugin.ts",
52
+ "entry": "./plugin.js",
53
53
  "engine": "^1.0.0",
54
54
  "runtime": "trusted",
55
55
  "features": [
package/plugin.js ADDED
@@ -0,0 +1,38 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineGamePlugin } from '@zhin.js/game-kit';
3
+ import { defineHostTables } from "./lib/models.js";
4
+ import { gameServicesToken } from "./lib/runtime-store.js";
5
+ import { createServices, } from "./lib/session-service.js";
6
+ /**
7
+ * Plugin Runtime:
8
+ * - Commands under `commands/` are authoritative (help + start/continue/…).
9
+ * - DB: prefer databaseHostToken; else in-memory GameServices.
10
+ * - Choice middleware under `middlewares/` handles adv choice payloads (Sandbox action→text).
11
+ */
12
+ export default defineGamePlugin({
13
+ name: 'text-adventure',
14
+ metadata: {
15
+ displayName: 'Text Adventure',
16
+ },
17
+ game: {
18
+ id: 'adv',
19
+ title: '秘境探险',
20
+ icon: '🗺️',
21
+ description: '文字冒险,31 区域 · 15 结局 · 成就收集',
22
+ commandPrefix: '/冒险',
23
+ quickStart: '开始',
24
+ aliases: ['adv', '秘境'],
25
+ menus: [
26
+ { id: 'start', label: '🚪 开始冒险' },
27
+ { id: 'continue', label: '🔄 继续冒险' },
28
+ { id: 'map', label: '🗺️ 探索进度' },
29
+ { id: 'achievements', label: '🏅 成就' },
30
+ { id: 'help', label: '📖 玩法说明' },
31
+ ],
32
+ },
33
+ tables: ['adv_sessions', 'adv_profiles'],
34
+ servicesToken: gameServicesToken,
35
+ defineHostTables,
36
+ createServices: (database) => createServices(database),
37
+ session: (services) => services.sessions,
38
+ });
@@ -1,5 +1,8 @@
1
- import type { Message, Plugin } from '@zhin.js/core';
2
- import { channelKey } from '@zhin.js/game-kit';
1
+ import {
2
+ channelKey,
3
+ type GameMessageLike,
4
+ type GameReply,
5
+ } from '@zhin.js/game-kit';
3
6
  import {
4
7
  continueAdventure,
5
8
  sessionSummary,
@@ -21,11 +24,10 @@ export const ADV_HELP = [
21
24
  ].join('\n');
22
25
 
23
26
  export async function runAdvCommand(
24
- plugin: Plugin | null,
25
27
  services: GameServices,
26
- message: Message<any>,
28
+ message: GameMessageLike,
27
29
  action: string,
28
- ): Promise<string | undefined> {
30
+ ): Promise<GameReply> {
29
31
  const ch = channelKey(message);
30
32
  const userId = message.$sender.id;
31
33
  const userName = message.$sender.name?.trim() || userId;
@@ -44,11 +46,11 @@ export async function runAdvCommand(
44
46
  }
45
47
 
46
48
  if (action === 'start') {
47
- return startAdventure(plugin, services, message);
49
+ return (await startAdventure(services, message)) ?? '';
48
50
  }
49
51
 
50
52
  if (action === 'continue') {
51
- return continueAdventure(plugin, services, message);
53
+ return continueAdventure(services, message);
52
54
  }
53
55
 
54
56
  if (action === 'map') {
@@ -70,12 +72,3 @@ export async function runAdvCommand(
70
72
 
71
73
  return `未知子命令:${action}\n\n${ADV_HELP}`;
72
74
  }
73
-
74
- /** Plugin Runtime / smoke: text-only, no Adapter.editMessage. */
75
- export async function runAdvCommandText(
76
- services: GameServices,
77
- message: Message<any>,
78
- action: string,
79
- ): Promise<string> {
80
- return (await runAdvCommand(null, services, message, action)) ?? '';
81
- }
package/src/game-flow.ts CHANGED
@@ -1,5 +1,4 @@
1
- import type { Adapter, Message, Plugin } from '@zhin.js/core';
2
- import { plainTextFromSendContent } from '@zhin.js/game-kit';
1
+ import type { GameMessageLike, GameReply } from '@zhin.js/game-kit';
3
2
  import type { AdvProfileRow, AdvSessionRow } from './models.js';
4
3
  import { formatNewAchievements } from './profile-format.js';
5
4
  import { buildSceneInteractive } from './scene-view.js';
@@ -8,54 +7,30 @@ import {
8
7
  getScene,
9
8
  resolveChoice,
10
9
  stateFromSession,
10
+ visibleChoices,
11
11
  } from './story.js';
12
12
  import type { GameServices } from './session-service.js';
13
13
 
14
14
  export async function sendOrEditScene(
15
- plugin: Plugin | null,
16
15
  services: GameServices,
17
- message: Message<any>,
16
+ message: GameMessageLike,
18
17
  session: AdvSessionRow,
19
18
  extraNarrative = '',
20
19
  profile?: AdvProfileRow,
21
- ): Promise<string> {
20
+ ): Promise<GameReply> {
22
21
  const prof = profile
23
22
  ?? await services.profiles.getOrCreate(session.player_id, session.player_name);
24
23
  const content = buildSceneInteractive(session, extraNarrative, prof, message.$channel.type);
25
24
  if (!content) {
26
25
  return '场景数据异常,请 adv quit 后重新开始。';
27
26
  }
28
- if (!plugin) return plainTextFromSendContent(content);
29
-
30
- const adapter = plugin.root.inject(message.$adapter) as Adapter;
31
-
32
- if (session.board_message_id) {
33
- const msgId = await adapter.editMessage({
34
- messageId: session.board_message_id,
35
- context: String(message.$adapter),
36
- endpoint: message.$endpoint,
37
- id: message.$channel.id,
38
- type: message.$channel.type,
39
- content,
40
- });
41
- if (msgId !== session.board_message_id) {
42
- await services.sessions.updateSession(session.id, { board_message_id: msgId });
43
- }
44
- return msgId;
45
- }
46
-
47
- const msgId = await message.$reply?.(content);
48
- if (msgId) {
49
- await services.sessions.updateSession(session.id, { board_message_id: msgId });
50
- }
51
- return msgId ?? '';
27
+ return content;
52
28
  }
53
29
 
54
30
  export async function startAdventure(
55
- plugin: Plugin | null,
56
31
  services: GameServices,
57
- message: Message<any>,
58
- ): Promise<string | undefined> {
32
+ message: GameMessageLike,
33
+ ): Promise<GameReply | undefined> {
59
34
  const ch = `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`;
60
35
  const active = await services.sessions.getActiveByChannel(ch);
61
36
  if (active) {
@@ -73,19 +48,18 @@ export async function startAdventure(
73
48
  'start',
74
49
  [],
75
50
  );
76
- const text = await sendOrEditScene(plugin, services, message, session);
77
- return plugin ? undefined : text;
51
+ return sendOrEditScene(services, message, session);
78
52
  }
79
53
 
80
54
  export async function handleChoice(
81
- plugin: Plugin | null,
82
55
  services: GameServices,
83
- message: Message<any>,
56
+ message: GameMessageLike,
84
57
  sessionId: string,
85
58
  choiceId: string,
86
- ): Promise<string | null> {
59
+ ): Promise<GameReply | null> {
87
60
  const session = await services.sessions.getById(sessionId);
88
- if (!session || session.status !== 'active') {
61
+ // 踏进 terminal 场景时 status 必为 completed,终局需放行「再玩一次」(restart)
62
+ if (!session || (session.status !== 'active' && !(session.status === 'completed' && choiceId === 'restart'))) {
89
63
  return '冒险不存在或已结束。';
90
64
  }
91
65
  if (session.player_id !== message.$sender.id) {
@@ -109,13 +83,16 @@ export async function handleChoice(
109
83
  ending_id: '',
110
84
  status: 'active',
111
85
  step_count: 0,
112
- board_message_id: '',
113
86
  });
114
87
  const updated = (await services.sessions.getById(session.id))!;
88
+ await services.profiles.onRunStarted(updated.player_id, updated.player_name);
115
89
  await services.profiles.onStep(updated.player_id, updated.player_name, 'start', []);
116
- const text = await sendOrEditScene(plugin, services, message, updated);
117
- // text-only 模式(plugin===null)下 sendOrEditScene 的唯一输出就是返回文本
118
- return plugin ? null : text;
90
+ return sendOrEditScene(services, message, updated);
91
+ }
92
+
93
+ // 服务端校验:选项必须当前可见(requires 门槛不能只靠视图层过滤,payload 可绕过)
94
+ if (!visibleChoices(scene, state).some((c) => c.id === choiceId)) {
95
+ return '该选项不可用。';
119
96
  }
120
97
 
121
98
  const result = resolveChoice(state, choiceId);
@@ -163,24 +140,19 @@ export async function handleChoice(
163
140
 
164
141
  const updated = (await services.sessions.getById(session.id))!;
165
142
  const extra = formatNewAchievements(newAchievements);
166
- const text = await sendOrEditScene(plugin, services, message, updated, extra);
167
- // text-only 模式(plugin===null)下 sendOrEditScene 的唯一输出就是返回文本
168
- return plugin ? null : text;
143
+ return sendOrEditScene(services, message, updated, extra);
169
144
  }
170
145
 
171
146
  export async function continueAdventure(
172
- plugin: Plugin | null,
173
147
  services: GameServices,
174
- message: Message<any>,
175
- ): Promise<string> {
148
+ message: GameMessageLike,
149
+ ): Promise<GameReply> {
176
150
  const session = await services.sessions.getActiveForUser(
177
151
  `${message.$adapter}-${message.$endpoint}-${message.$channel.type}:${message.$channel.id}`,
178
152
  message.$sender.id,
179
153
  );
180
154
  if (!session) return '你没有进行中的冒险,发送 adv start 开始。';
181
- const text = await sendOrEditScene(plugin, services, message, session);
182
- if (!plugin) return text;
183
- return '已刷新当前场景。';
155
+ return sendOrEditScene(services, message, session);
184
156
  }
185
157
 
186
158
  export function sessionSummary(session: AdvSessionRow): string {
package/src/index.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export { ADV_HELP } from './adv-command.js';
2
2
  export { gameServicesToken, resolveGameServices } from './runtime-store.js';
3
- export { createInMemoryAdvDb, mountAdvMemoryServices } from './memory-db.js';
4
3
 
5
4
  /**
6
5
  * 已由 plugin.ts 接线:commands/ 命令、middlewares/ 文本与选项中间件、
package/src/models.ts CHANGED
@@ -19,7 +19,6 @@ declare module '@zhin.js/core' {
19
19
  flags: string;
20
20
  ending_id: string;
21
21
  status: AdvSessionStatus;
22
- board_message_id: string;
23
22
  step_count: number;
24
23
  updated_at: number;
25
24
  created_at: number;
@@ -64,7 +63,6 @@ export function defineHostTables(
64
63
  flags: { type: 'text', default: '{}' },
65
64
  ending_id: { type: 'text', default: '' },
66
65
  status: { type: 'text', default: 'active' },
67
- board_message_id: { type: 'text', default: '' },
68
66
  step_count: { type: 'integer', default: 0 },
69
67
  updated_at: { type: 'integer', default: 0 },
70
68
  created_at: { type: 'integer', default: 0 },
@@ -1,5 +1,11 @@
1
- import type { Database, Message, Models, RelatedModel } from '@zhin.js/core';
2
- import { channelKey, generateSessionId, boardMessageMatches } from '@zhin.js/game-kit';
1
+ import type { Database, Models, RelatedModel } from '@zhin.js/core';
2
+ import {
3
+ BaseSessionService,
4
+ channelKey,
5
+ generateSessionId,
6
+ type GameMessageLike,
7
+ type GameSessionDatabase,
8
+ } from '@zhin.js/game-kit';
3
9
  import type { AdvModelName, AdvSessionRow } from './models.js';
4
10
  import { createProfileService, ProfileService } from './profile-service.js';
5
11
 
@@ -17,37 +23,16 @@ export interface GameServices {
17
23
  profiles: ProfileService;
18
24
  }
19
25
 
20
- export class SessionService {
21
- constructor(private readonly db: AdvDatabase) {}
22
-
23
- async getActiveByChannel(channel: string): Promise<AdvSessionRow | null> {
24
- const rows = await getModel(this.db, 'adv_sessions').findAll({
25
- channel_key: channel,
26
- status: 'active',
26
+ export class SessionService extends BaseSessionService<AdvSessionRow> {
27
+ constructor(db: AdvDatabase) {
28
+ super(db as unknown as GameSessionDatabase<AdvSessionRow>, {
29
+ gameId: 'adv',
30
+ table: 'adv_sessions',
31
+ userFields: ['player_id'],
27
32
  });
28
- return rows[0] ?? null;
29
- }
30
-
31
- async getActiveForUser(channel: string, userId: string): Promise<AdvSessionRow | null> {
32
- const row = await this.getActiveByChannel(channel);
33
- if (!row || row.player_id !== userId) return null;
34
- return row;
35
33
  }
36
34
 
37
- async getById(id: string): Promise<AdvSessionRow | null> {
38
- return getModel(this.db, 'adv_sessions').findOne({ id });
39
- }
40
-
41
- async getActiveByBoardMessageId(messageId: string): Promise<AdvSessionRow | null> {
42
- if (!messageId) return null;
43
- const rows = await getModel(this.db, 'adv_sessions').findAll({});
44
- for (const row of rows) {
45
- if (boardMessageMatches(row.board_message_id ?? '', messageId)) return row;
46
- }
47
- return null;
48
- }
49
-
50
- async createSession(message: Message<any>): Promise<AdvSessionRow> {
35
+ async createSession(message: GameMessageLike): Promise<AdvSessionRow> {
51
36
  const now = Date.now();
52
37
  const id = generateSessionId();
53
38
  const row: AdvSessionRow = {
@@ -65,34 +50,11 @@ export class SessionService {
65
50
  flags: '{}',
66
51
  ending_id: '',
67
52
  status: 'active',
68
- board_message_id: '',
69
53
  step_count: 0,
70
54
  updated_at: now,
71
55
  created_at: now,
72
56
  };
73
- await getModel(this.db, 'adv_sessions').create(row);
74
- return row;
75
- }
76
-
77
- async updateSession(id: string, patch: Partial<AdvSessionRow>): Promise<void> {
78
- await getModel(this.db, 'adv_sessions').updateWhere(
79
- { id },
80
- { ...patch, updated_at: Date.now() },
81
- );
82
- }
83
-
84
- async abortStale(idleMs: number): Promise<number> {
85
- const cutoff = Date.now() - idleMs;
86
- const sessions = getModel(this.db, 'adv_sessions');
87
- const rows = await sessions.findAll({ status: 'active' });
88
- let n = 0;
89
- for (const row of rows) {
90
- if (row.updated_at < cutoff) {
91
- await sessions.updateWhere({ id: row.id }, { status: 'aborted', updated_at: Date.now() });
92
- n++;
93
- }
94
- }
95
- return n;
57
+ return this.createRow(row);
96
58
  }
97
59
  }
98
60
 
@@ -102,4 +64,3 @@ export function createServices(db: AdvDatabase): GameServices {
102
64
  profiles: createProfileService(db),
103
65
  };
104
66
  }
105
-
@@ -1,6 +0,0 @@
1
- import type { DatabaseHost } from '@zhin.js/plugin-runtime';
2
- import { type AdvDatabase, type GameServices } from './session-service.js';
3
- /** In-memory adv tables for Plugin Runtime slice-2 (no DatabaseFeature). */
4
- export declare function createInMemoryAdvDb(): AdvDatabase;
5
- export declare function mountAdvMemoryServices(): GameServices;
6
- export declare function mountAdvHostServices(host: DatabaseHost): GameServices;
package/lib/memory-db.js DELETED
@@ -1,18 +0,0 @@
1
- import { createHostGameDb, createInMemoryGameDb } from '@zhin.js/game-kit';
2
- import { defineHostTables } from './models.js';
3
- import { createServices } from './session-service.js';
4
- const ADV_TABLES = ['adv_sessions', 'adv_profiles'];
5
- /** In-memory adv tables for Plugin Runtime slice-2 (no DatabaseFeature). */
6
- export function createInMemoryAdvDb() {
7
- return createInMemoryGameDb(ADV_TABLES);
8
- }
9
- export function mountAdvMemoryServices() {
10
- const services = createServices(createInMemoryAdvDb());
11
- return services;
12
- }
13
- export function mountAdvHostServices(host) {
14
- defineHostTables(host);
15
- const services = createServices(createHostGameDb(host, ADV_TABLES));
16
- return services;
17
- }
18
- //# sourceMappingURL=memory-db.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-db.js","sourceRoot":"","sources":["../src/memory-db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAuC,MAAM,sBAAsB,CAAC;AAE3F,MAAM,UAAU,GAAG,CAAC,cAAc,EAAE,cAAc,CAAU,CAAC;AAE7D,4EAA4E;AAC5E,MAAM,UAAU,mBAAmB;IACjC,OAAO,oBAAoB,CAAC,UAAU,CAA2B,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,QAAQ,GAAG,cAAc,CAAC,mBAAmB,EAAE,CAAC,CAAC;IACvD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAkB;IACrD,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,QAAQ,GAAG,cAAc,CAAC,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAA2B,CAAC,CAAC;IAC9F,OAAO,QAAQ,CAAC;AAClB,CAAC"}
package/plugin.ts DELETED
@@ -1,65 +0,0 @@
1
- import { definePlugin, databaseHostToken, scheduleHostToken } from '@zhin.js/plugin-runtime';
2
- import {
3
- registerRuntimeGame,
4
- initGameRecordHost,
5
- DEFAULT_GAME_STALE_CRON,
6
- DEFAULT_GAME_STALE_IDLE_MS,
7
- } from '@zhin.js/game-kit';
8
- import { mountAdvHostServices, mountAdvMemoryServices } from './src/memory-db.js';
9
- import { gameServicesToken } from './src/runtime-store.js';
10
- import type { GameServices } from './src/session-service.js';
11
-
12
- /**
13
- * Plugin Runtime:
14
- * - Commands under `commands/` are authoritative (help + start/continue/…).
15
- * - DB: prefer databaseHostToken; else in-memory GameServices.
16
- * - Choice middleware under `middlewares/` handles adv choice payloads (Sandbox action→text).
17
- */
18
- export default definePlugin({
19
- name: 'text-adventure',
20
- metadata: {
21
- displayName: 'Text Adventure',
22
- },
23
- setup(context) {
24
- let services: GameServices;
25
- if (context.resources.has(databaseHostToken)) {
26
- const host = context.resources.use(databaseHostToken);
27
- services = mountAdvHostServices(host);
28
- context.lifecycle.add(initGameRecordHost(host));
29
- } else {
30
- services = mountAdvMemoryServices();
31
- }
32
- context.resources.provide(gameServicesToken, services);
33
- const disposeHub = registerRuntimeGame({
34
- id: 'adv',
35
- title: '秘境探险',
36
- icon: '🗺️',
37
- description: '文字冒险,31 区域 · 15 结局 · 成就收集',
38
- commandPrefix: '/冒险',
39
- quickStart: '开始',
40
- aliases: ['adv', '秘境'],
41
- menus: [
42
- { id: 'start', label: '🚪 开始冒险' },
43
- { id: 'continue', label: '🔄 继续冒险' },
44
- { id: 'map', label: '🗺️ 探索进度' },
45
- { id: 'achievements', label: '🏅 成就' },
46
- { id: 'help', label: '📖 玩法说明' },
47
- ],
48
- });
49
- context.lifecycle.add(disposeHub);
50
-
51
- if (context.resources.has(scheduleHostToken)) {
52
- const schedule = context.resources.use(scheduleHostToken);
53
- const disposeCron = schedule.register({
54
- id: 'adv/abort-stale',
55
- cron: DEFAULT_GAME_STALE_CRON,
56
- description: 'Abort stale text-adventure sessions',
57
- async execute() {
58
- if (!services.sessions.abortStale) return;
59
- await services.sessions.abortStale(DEFAULT_GAME_STALE_IDLE_MS);
60
- },
61
- });
62
- context.lifecycle.add(disposeCron);
63
- }
64
- },
65
- });
package/src/memory-db.ts DELETED
@@ -1,22 +0,0 @@
1
- import { createHostGameDb, createInMemoryGameDb } from '@zhin.js/game-kit';
2
- import type { DatabaseHost } from '@zhin.js/plugin-runtime';
3
- import { defineHostTables } from './models.js';
4
- import { createServices, type AdvDatabase, type GameServices } from './session-service.js';
5
-
6
- const ADV_TABLES = ['adv_sessions', 'adv_profiles'] as const;
7
-
8
- /** In-memory adv tables for Plugin Runtime slice-2 (no DatabaseFeature). */
9
- export function createInMemoryAdvDb(): AdvDatabase {
10
- return createInMemoryGameDb(ADV_TABLES) as unknown as AdvDatabase;
11
- }
12
-
13
- export function mountAdvMemoryServices(): GameServices {
14
- const services = createServices(createInMemoryAdvDb());
15
- return services;
16
- }
17
-
18
- export function mountAdvHostServices(host: DatabaseHost): GameServices {
19
- defineHostTables(host);
20
- const services = createServices(createHostGameDb(host, ADV_TABLES) as unknown as AdvDatabase);
21
- return services;
22
- }