import BaseController from '../../common/base.controller' import { role, router } from '../../decorators/router' import { Puzzle } from '../../models/content/Puzzle' import { PuzzleSession, PuzzleStatusClass } from '../../models/match/PuzzleSession' import { ZError } from '../../common/ZError' import { broadcast, createRoom, joinRoom, updateScore } from '../../services/WsSvr' import { RoomState } from '../../services/RoomState' import { retry } from '../../utils/promise.util' import { RoomLockErr } from '../../common/RoomLockErr' import { Schedule } from '../../clock/Schedule' import { calcPvpScore, calcSingleScore, checkSingleFinish, fetchLevelCfg, fetchSinglePuzzleType, getRank, startGame, transformRecord, updateSingleRank } from '../../services/GameLogic' import { Shop, validShopId } from '../../models/shop/Shop' class PuzzleController extends BaseController { @role('anon') @router('get /api/test') async test(req) { // try{ // console.time('ss') // const nanoid = customAlphabet('2345678abcdefghjkmnpqrstwxy', 10) // for (let i = 0; i < 100; i++) { // console.log(nanoid()) // } // console.timeEnd('ss') // } catch (err) { // } let shop = await Shop.find({}, {_id: 1}).limit(1) return shop[0] } @role('anon') @router('post /api/:accountid/puzzle/list') async list(req, res) { let { shop, level, accountid, type } = req.params level = +level || 1 if (!shop || !validShopId(shop)) { throw new ZError(10, '没有店铺id或者店铺id格式不正确, 测试使用: 607ff59d4a4e16687a3b7079') } const cfg = fetchLevelCfg(level) let params = {} let typeArr = await fetchSinglePuzzleType({shopId: shop, levelCfg: cfg}) if (typeArr?.length > 0) { params = {$or: [{tag: {$in: typeArr}}, {sub_tag: {$in: typeArr}}]} } let record1 = await Puzzle.randomQuestions(Object.assign(params, {quality: 1}), 5) let record2 = await Puzzle.randomQuestions(Object.assign(params, {quality: 2}), 5) let record3 = await Puzzle.randomQuestions(Object.assign(params, {quality: 3}), 5) const records = (record1.concat(record2)).concat(record3) let history = new PuzzleSession({ shop, level }) history.members.set(accountid, new PuzzleStatusClass()) for (let record of records) { history.questions.set(record.id, record.a1) } history.expire = Date.now() + (cfg.time || 90) * 1000 history.type = 0 history.difficultyMode = type || 0 history.qtypes = typeArr await history.save() const results = transformRecord(records) return { session: history.id, records: results } } @role('anon') @router('post /api/:accountid/puzzle/answer') async report(req, res) { let { id, answer, type, session, accountid, mode } = req.params mode = mode || 0 if (!id || !session) { throw new ZError(11, 'param mismatch') } let history = await PuzzleSession.findById(session) if (!history) { throw new ZError(13, 'not found match info') } if (!history.members.has(accountid)) { throw new ZError(14, 'not in current match') } if (!history.questions.has(id)) { throw new ZError(16, 'current question not in current match') } let record = history.questions.get(id) let result = record === answer ? 1 : 0 if (type == 1) { // type = 1 为客户端上报的超时消息, 直接判负 result = 0 } if (history.status == 9 || history.hasExpired()) { result = 0 } let statMap = history.members.get(accountid) if (statMap.answer.has(id)) { throw new ZError(15, 'current question already answered') } statMap.timeLast = Date.now() statMap.answer.set(id, result) if (result == 1) { statMap.rightCount++ statMap.comboCount++ statMap.maxCombo = Math.max(statMap.maxCombo, statMap.comboCount) } else { statMap.errorCount++ statMap.comboCount = 0 } history.status = 1 history.markModified('members') let gameResult = 0 await history.save() let rspData: any = { result, answer: record, stats: history.members } if (mode == 1) { let score = result ? calcPvpScore(history.scheduleKey, statMap.comboCount) : 0 await broadcast(history.room, 'answer', {accountid, result, score}) await updateScore(history.room, [{accountid, score }]) // if (checkSubFinish(history, id)) { // await sendOneQuestion(history) // } } else if (mode == 0) { gameResult = checkSingleFinish(history, accountid) if (gameResult) { history.status = 9 if (gameResult === 1) { let { score } = calcSingleScore(history, accountid) let rankObj = { shop: history.shop, level: history.level, accountId: accountid, score, mode: 0, session: history.id } await updateSingleRank(rankObj) } history.markModified('members') await history.save() let {rankList, userRank } = await getRank({shop: history.shop, level: history.level, accountId: accountid, mode: 0, skip: 0, limit: 20}) rspData.rankList = rankList rspData.userRank = userRank } } rspData.gameResult = gameResult return rspData } @role('anon') @router('post /api/:accountid/puzzle/more') async moreLevelQuestions(req, res) { let {session,accountid, count, quality} = req.params count = count ? +count : 10 if (!session) { throw new ZError(11, 'param mismatch') } let history = await PuzzleSession.findById(session) if (!history) { throw new ZError(13, 'not found match info') } if (history.status == 9 || history.hasExpired()) { history.status = 9 await history.save() throw new ZError(17, 'match end') } if (!history.members.has(accountid)) { throw new ZError(14, 'not in current match') } let ids = Array.from(history.questions.keys()) let options: any = {'_id': {$nin: ids}} if (quality) { options.quality = quality } if (history.qtypes.length > 0) { Object.assign(options, {$or: [{tag: {$in: history.qtypes}}, {sub_tag: {$in: history.qtypes}}]}) } let records = await Puzzle.randomQuestions(options, count) const results = transformRecord(records) for (let record of records) { history.questions.set(record.id, record.a1) } history.markModified('questions') await history.save() return results } @role('anon') @router('post /api/:accountid/puzzle/match') async joinMultipleGame(req, res) { const { shop, aid, accountid, debug_begin_sec, debug_qcount } = req.params let data: any = { shop, maxTime: 3600, accountid } let beginSecond = (debug_begin_sec ? +debug_begin_sec : 20) beginSecond = (Math.max(2, beginSecond)) * 1000 /** * 查找店铺设置, 查看一定时间内是否有要开始的活动 * 如果有, 则读取配置, 加入开始游戏的定时 * 如果没有人请求, 说明没人参加, 也没必要开启定时了 * TODO:: 读取店铺活动配置 */ let activityId = '1111111' let roomId = '' let sessionId = '' let sessionMatch = '' let beginTime = 0 let result = new RoomState().isLock(shop) try { await retry>(async () => { if (result) { throw new RoomLockErr('') } new RoomState().lock(shop) let history = await PuzzleSession.findOne({ shop, status: {$in: [0, 1]}, type: 1 }) if (history) { // if (history && !history.hasExpired()) { beginTime = history.begin sessionMatch = history.id if (!history.members.has(accountid)) { let rsp = await joinRoom({roomId: history.room}) if (rsp.status != 200) { new RoomState().unlock(shop) throw new ZError(11, 'error create room') } let memberData = new PuzzleStatusClass() sessionId = rsp.data?.sessionId memberData.sessionId = sessionId history.members.set(accountid, memberData) history.markModified('members') await history.save() new RoomState().unlock(shop) return roomId = history.room } else { let memberData = history.members.get(accountid) sessionId = memberData.sessionId roomId = history.room new RoomState().unlock(shop) return } } else { let rsp = await createRoom(data) if (rsp.status != 200) { new RoomState().unlock(shop) throw new ZError(11, 'error create room') } roomId = rsp.data?.room?.roomId sessionId = rsp.data?.sessionId history = new PuzzleSession({shop, status: 0, type: 1}) let memberData = new PuzzleStatusClass() memberData.sessionId = sessionId history.members.set(accountid, memberData) history.room = roomId //TODO: 根据配置赋值 history.total = debug_qcount || 10 history.begin = Date.now() + beginSecond beginTime = history.begin history.expire = history.begin + (100 || 90) * 1000 history.type = 1 await history.save() sessionMatch = history.id new Schedule().beginSchedule(beginSecond, async function () { await startGame(roomId, history.id) }, shop) new RoomState().unlock(shop) return roomId } }, 0, [RoomLockErr]) } catch (err) { new RoomState().unlock(shop) throw new ZError(12, 'error create room') } return { roomId, beginTime,sessionId, session: sessionMatch } } @role('anon') @router('post /api/:accountId/puzzle/rank') async singleRank(req) { let {shop, level, accountId, mode, skip, limit} = req.params skip = +skip || 0 limit = +limit || 10 let {rankList, userRank, rankTotal} = await getRank({shop, level, accountId, mode, skip, limit}) return {rankList, userRank, rankTotal} } }