From e16b328892705b462bc6f3228a1adf93685bf7c1 Mon Sep 17 00:00:00 2001 From: wancat Date: Wed, 30 Aug 2023 18:05:11 +0800 Subject: [PATCH] feat: refactor stage to multiple route design --- quests/helloworld/game.yml | 18 ++++--- .../src/api/controllers/commandController.js | 2 +- server/src/game/commandHandler.js | 52 ++++++++++++------- server/src/models/quest.js | 6 +++ server/src/models/session.js | 3 +- server/swagger.yaml | 2 +- 6 files changed, 51 insertions(+), 32 deletions(-) diff --git a/quests/helloworld/game.yml b/quests/helloworld/game.yml index 2dca81c..fcc4d7f 100644 --- a/quests/helloworld/game.yml +++ b/quests/helloworld/game.yml @@ -8,7 +8,7 @@ stages: - id: helloworld condition: command: - - echo Hello World! + - echo Hello World responses: - type: narrative content: @@ -22,12 +22,13 @@ stages: - 由於長久以來依賴於薩加,世人已失去了「指令」的力量,即將走向衰敗和滅亡的命運。 - 你的任務,就是找回被世人遺忘的指令,奪回 Linux 之根,成為 Linux 之王。 - 首先,你必須要學會如何使用指令。 - - 一個指令是由一個指令名稱和一些參數所組成的,而指令名稱和參數之間是以空白字元分隔。 - - 例如,你可以輸入 `ls` 來查看目前的目錄下有哪些檔案。 + - 你要學習的第一個指令,是「發現」的指令:`ls`。ls 可以用來查看一個目錄中有哪些檔案。 + - 現在,請你輸入 `ls` 來查看目前的目錄下有哪些檔案。 hints: - 使用 `ls` 指令來查看目前的目錄下有哪些檔案。 - next: ls - id: ls + requirements: + - helloworld condition: command: - ls @@ -47,8 +48,9 @@ stages: hints: - 使用 `cat [檔案名稱]` 指令來查看檔案內容。 - 使用 `Tab` 鍵來取得指令提示。 - next: cat - id: cat + requirements: + - ls condition: command: - cat forgotten_scroll.txt @@ -58,8 +60,9 @@ stages: content: - 你找到的是一個被遺忘的卷軸,上面寫著解除封印的咒語。 - 使用你已經知道的 `echo` 指令來詠唱咒語。 - next: echo - - id: echo + - id: END + requirements: + - cat condition: command: - echo Unix, rise and guide me! @@ -72,4 +75,3 @@ stages: content: - 恭喜您,我的指令官,您已成功解除了封印。 - 請跟著在接下了關卡,一步步取回 Linux 之根吧! - next: END diff --git a/server/src/api/controllers/commandController.js b/server/src/api/controllers/commandController.js index acfdeb2..def7b1b 100644 --- a/server/src/api/controllers/commandController.js +++ b/server/src/api/controllers/commandController.js @@ -36,7 +36,6 @@ export async function newCommand(req, res) { } session.lastActivityAt = new Date() - await session.save() const c = new Command({ session, @@ -54,4 +53,5 @@ export async function newCommand(req, res) { console.log(response) res.status(201).json(response) + await session.save() } diff --git a/server/src/game/commandHandler.js b/server/src/game/commandHandler.js index 62eb626..e1aa818 100644 --- a/server/src/game/commandHandler.js +++ b/server/src/game/commandHandler.js @@ -50,20 +50,15 @@ export default class CommandHandler { const graph = new FileGraph(this.session.graph) graph.discover(this.additionalData.discover) this.session.graph = graph - await this.session.save() } - async run() { - const quest = await Quest.findById(this.session.quest) - const stage = quest.stages.find((s) => s.id === this.session.progress) - if (!stage) { - console.error('stage not found', this.session.progress) - return {} - } - - this.handleEvent() - this.handleCommand() + getStages() { + return this.quest.stages.filter((s) => + s.requirements.every((r) => this.session.completed.includes(r)) + ) + } + matchStage(stage) { const commandMatch = checkMatch( stage.condition.command, this.commandInput.command @@ -79,22 +74,39 @@ export default class CommandHandler { if (!commandMatch || !outputMatch || !errorMatch) return {} - this.session.completion.push(this.session.progress) + this.session.completed.push(stage.id) this.session.hints.push(...stage.hints) - - this.session.progress = stage.next - await this.session.save() - - if (stage.next === 'END') { + if (stage.id === 'END') { this.session.status = 'finished' this.session.finishedAt = new Date() - await this.session.save() } - return { responses: stage.responses, hints: stage.hints, - end: stage.next === 'END', + } + } + + async run() { + this.quest = await Quest.findById(this.session.quest) + const stages = this.getStages() + if (stages.length === 0) { + console.error('stage not found', this.session.progress) + return {} + } + + console.log('stages', stages) + + this.handleEvent() + this.handleCommand() + + const response = stages.reduce( + (r, s) => ({ ...r, ...this.matchStage(s) }), + {} + ) + + return { + end: this.session.status === 'finished', + ...response, } } } diff --git a/server/src/models/quest.js b/server/src/models/quest.js index ea592f1..434a9e5 100644 --- a/server/src/models/quest.js +++ b/server/src/models/quest.js @@ -15,6 +15,12 @@ const stageSchema = new Schema({ type: String, required: true, }, + requirements: [ + { + type: String, + required: true, + }, + ], condition: { command: { type: [String], diff --git a/server/src/models/session.js b/server/src/models/session.js index 27d7efa..5aeac2f 100644 --- a/server/src/models/session.js +++ b/server/src/models/session.js @@ -39,8 +39,7 @@ const Session = model( default: Date.now, required: true, }, - progress: String, - completion: [String], + completed: [String], hints: [String], graph: { type: nodeSchema, diff --git a/server/swagger.yaml b/server/swagger.yaml index 34f9bf1..69e65a8 100644 --- a/server/swagger.yaml +++ b/server/swagger.yaml @@ -392,7 +392,7 @@ components: type: string description: current stage id example: helloworld - completion: + completed: type: array description: list of completed stage ids items: