diff --git a/.eslintrc.js b/.eslintrc.js index 7410a96..0d2e94d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,5 +28,6 @@ module.exports = { 'no-plusplus': 'off', 'prettier/prettier': ['error', { endOfLine: 'auto', semi: false }], 'import/prefer-default-export': 'off', + 'no-restricted-syntax': ['off', 'ForOfStatement'], }, } diff --git a/packages/models/quest.js b/packages/models/quest.js index 4c500bd..efdd130 100644 --- a/packages/models/quest.js +++ b/packages/models/quest.js @@ -35,6 +35,26 @@ conditionSchema.add({ not: conditionSchema, }) +const exceptionSchema = new Schema({ + condition: { + type: conditionSchema, + required: false, + }, + catchAll: { + type: Boolean, + required: true, + default: false, + }, + responses: { + type: [responseSchema], + required: true, + }, + hints: { + type: [String], + required: true, + }, +}) + const stageSchema = new Schema({ id: { type: String, @@ -66,6 +86,10 @@ const stageSchema = new Schema({ type: [String], required: true, }, + exceptions: { + type: [exceptionSchema], + required: true, + }, }) const Quest = model( diff --git a/quests/copy/Dockerfile b/quests/copy/Dockerfile new file mode 100644 index 0000000..2a8e341 --- /dev/null +++ b/quests/copy/Dockerfile @@ -0,0 +1,3 @@ +FROM linuxodyssey/quest-base + +COPY home ${HOME} \ No newline at end of file diff --git a/quests/copy/game.yml b/quests/copy/game.yml index 923f03f..d7039c5 100644 --- a/quests/copy/game.yml +++ b/quests/copy/game.yml @@ -1,6 +1,27 @@ title: 複製檔案 instruction: | - 複製檔案 + 在這個關卡中,你將學習如何複製檔案。 requirements: - create + +stages: + - id: start + task: 輸入 `echo start` 來開始 + condition: + output: + - start + exceptions: + - condition: + command: + - ^echo$ + responses: + - type: narrative + content: + - 你忘記輸入 `start` 了嗎? + - 請輸入 `echo start` 來開始 + - catchAll: true + responses: + - type: narrative + content: + - 請輸入 `echo start` 來開始 diff --git a/server/src/game/commandHandler.js b/server/src/game/commandHandler.js index d03f6f1..26f4526 100644 --- a/server/src/game/commandHandler.js +++ b/server/src/game/commandHandler.js @@ -97,6 +97,19 @@ export default class CommandHandler extends SessionHandler { return matches.some((m) => m === true) } + async checkException(stage) { + const exceptions = stage.exceptions || [] + for (const exception of exceptions) { + console.log(exception) + // eslint-disable-next-line no-await-in-loop + if (exception.condition && (await this.isMatch(exception.condition))) { + return exception + } + if (exception.catchAll) return exception + } + return null + } + async run() { this.quest = await Quest.findById(this.session.quest) const stages = this.getStages() @@ -114,6 +127,14 @@ export default class CommandHandler extends SessionHandler { const stage = stages.find((_, i) => matches[i]) if (!stage) { + for (const s of stages) { + // eslint-disable-next-line no-await-in-loop + const exception = await this.checkException(s) + if (exception) { + console.log('Got exception', exception) + return this.executeException(exception) + } + } return {} } const response = this.execute(stage) diff --git a/server/src/game/sessionHandler.js b/server/src/game/sessionHandler.js index 0688041..2312c4c 100644 --- a/server/src/game/sessionHandler.js +++ b/server/src/game/sessionHandler.js @@ -63,4 +63,19 @@ export default class SessionHandler { }, } } + + executeException(exception) { + const { hints, responses } = exception + if (hints && hints.length > 0) this.session.hints.push(hints) + + return { + responses, + hints, + callback: () => { + if (hints && hints.length > 0) + pushToSession(this.session.id, 'hints', hints) + pushToSession(this.session.id, 'status', this.session.status) + }, + } + } }