feat: load responses and tasks from existing session

This commit is contained in:
Justin Lin committed 2025-04-23 16:17:10 +08:00
1 parent 2fcca0c4b9
commit b4b8f8baee
9 files changed
+52 -28

No files matched your search

+16 -14
View File
@@ -43,7 +43,7 @@ const useSession = defineStore('session', {
}, },
async setSession(session: Session) { async setSession(session: Session) {
console.log('set session', session) console.log('set session', session)
this.session = { ...session, tasks: [] } this.session = session
// this.session.graph = new FileGraph(session.graph) // this.session.graph = new FileGraph(session.graph)
term.reset() term.reset()
await socket.connect(session) await socket.connect(session)
@@ -55,20 +55,22 @@ const useSession = defineStore('session', {
socket.emit('terminal', 'echo start\n') socket.emit('terminal', 'echo start\n')
}, },
async getActiveSession() { async getActiveSession() {
const session = await getActiveSession(this.questId) const session = await trpc.session.getActiveSession.query({
questId: this.questId,
})
if (session) { if (session) {
this.setSession(session) this.setSession(session)
} }
}, },
updateGraph(event: { discover?: FileObject[]; pwd?: string }) { // updateGraph(event: { discover?: FileObject[]; pwd?: string }) {
if (!this.session) return // if (!this.session) return
if (event.discover) { // if (event.discover) {
this.session.graph.discover(event.discover) // this.session.graph.discover(event.discover)
} // }
if (event.pwd) { // if (event.pwd) {
this.session.pwd = event.pwd // this.session.pwd = event.pwd
} // }
}, // },
newUpdate(update: SessionUpdate) { newUpdate(update: SessionUpdate) {
if (!this.session) return if (!this.session) return
this.session.responses = update.responses this.session.responses = update.responses
@@ -103,9 +105,9 @@ const useSession = defineStore('session', {
term.onData((data: string) => { term.onData((data: string) => {
socket.emit('terminal', data) socket.emit('terminal', data)
}) })
socket.on('graph', (event: { discover: FileObject[]; pwd: string }) => { // socket.on('graph', (event: { discover: FileObject[]; pwd: string }) => {
this.updateGraph(event) // this.updateGraph(event)
}) // })
socket.on('update', (update: SessionUpdate) => { socket.on('update', (update: SessionUpdate) => {
this.newUpdate(update) this.newUpdate(update)
}) })
+3 -3
View File
@@ -17,9 +17,9 @@ export interface StageResponse {
export interface Session { export interface Session {
_id: string _id: string
status: string status: string
graph: FileGraph // graph: FileGraph
pwd: string // pwd: string
hints: string[][] // hints: string[][]
tasks: ITask[] tasks: ITask[]
responses: IResponse[] responses: IResponse[]
} }
+1 -1
View File
@@ -8,7 +8,7 @@ import {
ITask, ITask,
} from './schema' } from './schema'
export class Session implements ISession { export class GameSession implements ISession {
private completed: string[] = [] private completed: string[] = []
private quest: Quest private quest: Quest
+7
View File
@@ -35,3 +35,10 @@ export const conditionSchema: z.ZodType<ICondition> =
export interface IFileExistenceChecker { export interface IFileExistenceChecker {
exists(file: IFileInput): Promise<boolean> exists(file: IFileInput): Promise<boolean>
} }
export class VoidFileExistenceChecker implements IFileExistenceChecker {
// eslint-disable-next-line class-methods-use-this
async exists(_file: IFileInput): Promise<boolean> {
return false
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
import { expect, it, describe } from '@jest/globals' import { expect, it, describe } from '@jest/globals'
import { IQuest } from '../src/schema' import { IQuest } from '../src/schema'
import { Session } from '../src/Session' import { GameSession } from '../src/Session'
import { MockFileChecker } from './asyncCondition.test' import { MockFileChecker } from './asyncCondition.test'
const quest: IQuest = { const quest: IQuest = {
@@ -65,7 +65,7 @@ const quest: IQuest = {
describe('exceptions', () => { describe('exceptions', () => {
it('should return the stage exception', async () => { it('should return the stage exception', async () => {
const session = new Session( const session = new GameSession(
{ completedEvents: [] }, { completedEvents: [] },
quest, quest,
new MockFileChecker() new MockFileChecker()
@@ -76,7 +76,7 @@ describe('exceptions', () => {
}) })
it('should return the catch all exception', async () => { it('should return the catch all exception', async () => {
const session = new Session( const session = new GameSession(
{ completedEvents: [] }, { completedEvents: [] },
quest, quest,
new MockFileChecker() new MockFileChecker()
@@ -87,7 +87,7 @@ describe('exceptions', () => {
}) })
it('should return the global exception', async () => { it('should return the global exception', async () => {
const session = new Session( const session = new GameSession(
{ completedEvents: [] }, { completedEvents: [] },
quest, quest,
new MockFileChecker() new MockFileChecker()
+5 -5
View File
@@ -1,6 +1,6 @@
import { expect, describe, it } from '@jest/globals' import { expect, describe, it } from '@jest/globals'
import { IQuest } from '../src/schema' import { IQuest } from '../src/schema'
import { Session } from '../src/Session' import { GameSession } from '../src/Session'
import { MockFileChecker } from './asyncCondition.test' import { MockFileChecker } from './asyncCondition.test'
const quest: IQuest = { const quest: IQuest = {
@@ -64,7 +64,7 @@ const quest: IQuest = {
describe('Session', () => { describe('Session', () => {
it('should return active stages', () => { it('should return active stages', () => {
const session = new Session( const session = new GameSession(
{ {
completedEvents: [], completedEvents: [],
}, },
@@ -84,7 +84,7 @@ describe('Session', () => {
}) })
it('should run command', async () => { it('should run command', async () => {
const session = new Session( const session = new GameSession(
{ {
completedEvents: [], completedEvents: [],
}, },
@@ -111,7 +111,7 @@ describe('Session', () => {
}) })
it('should return all responses', () => { it('should return all responses', () => {
const session = new Session( const session = new GameSession(
{ {
completedEvents: ['stage1'], completedEvents: ['stage1'],
}, },
@@ -135,7 +135,7 @@ describe('Session', () => {
}) })
it('should return tasks', () => { it('should return tasks', () => {
const session = new Session( const session = new GameSession(
{ {
completedEvents: [], completedEvents: [],
}, },
@@ -1,4 +1,4 @@
import { Session as GameSession } from '@linux-odyssey/game' import { GameSession } from '@linux-odyssey/game'
import { Command, Session } from '@linux-odyssey/models' import { Command, Session } from '@linux-odyssey/models'
import { matchedData } from 'express-validator' import { matchedData } from 'express-validator'
import type { Request, Response } from 'express' import type { Request, Response } from 'express'
+2
View File
@@ -2,6 +2,7 @@ import { router, publicProcedure } from '../trpc.js'
import { linkRouter } from './linkRouter.js' import { linkRouter } from './linkRouter.js'
import { questRouter } from './questRouter.js' import { questRouter } from './questRouter.js'
import { userRouter } from './userRouter.js' import { userRouter } from './userRouter.js'
import { sessionRouter } from './sessionRouter.js'
export const appRouter = router({ export const appRouter = router({
userList: publicProcedure.query(async (opts) => { userList: publicProcedure.query(async (opts) => {
@@ -11,6 +12,7 @@ export const appRouter = router({
links: linkRouter, links: linkRouter,
quests: questRouter, quests: questRouter,
users: userRouter, users: userRouter,
session: sessionRouter,
}) })
export type AppRouter = typeof appRouter export type AppRouter = typeof appRouter
+13
View File
@@ -1,9 +1,11 @@
import { TRPCError } from '@trpc/server' import { TRPCError } from '@trpc/server'
import { Session } from '@linux-odyssey/models' import { Session } from '@linux-odyssey/models'
import { GameSession, VoidFileExistenceChecker } from '@linux-odyssey/game'
import { z } from 'zod' import { z } from 'zod'
import { isQuestUnlocked } from '../models/sessionManager.js' import { isQuestUnlocked } from '../models/sessionManager.js'
import config from '../config.js' import config from '../config.js'
import { protectedProcedure, router } from '../trpc.js' import { protectedProcedure, router } from '../trpc.js'
import { questManager } from '../models/quest.js'
export const sessionRouter = router({ export const sessionRouter = router({
getActiveSession: protectedProcedure getActiveSession: protectedProcedure
@@ -27,6 +29,15 @@ export const sessionRouter = router({
if (!session) { if (!session) {
return null return null
} }
const quest = await questManager.get(questId)
if (!quest) {
return null
}
const gameSession = new GameSession(
session,
quest,
new VoidFileExistenceChecker()
)
return { return {
_id: session._id.toString(), _id: session._id.toString(),
user: session.user, user: session.user,
@@ -35,6 +46,8 @@ export const sessionRouter = router({
createdAt: session.createdAt, createdAt: session.createdAt,
lastActivityAt: session.lastActivityAt, lastActivityAt: session.lastActivityAt,
completedEvents: session.completedEvents, completedEvents: session.completedEvents,
responses: gameSession.getResponses(),
tasks: gameSession.getTasks(),
} }
} }
throw new TRPCError({ throw new TRPCError({