diff --git a/game-server/app.ts b/game-server/app.ts index 99c97bba7..3dc35b47c 100644 --- a/game-server/app.ts +++ b/game-server/app.ts @@ -12,6 +12,7 @@ import * as redis from 'redis'; import './app/servers/user.rpc.define' import * as routeUtil from './app/util/routeUtil'; import { preload } from './preload'; +var checkEventFilter = require('./app/servers/battle/filter/checkEventFilter'); // TODO 需要整理。 import _pinus = require('pinus'); @@ -136,7 +137,7 @@ app.configure('production|development', function () { return; } cb(null); - }) + }); // route configures app.route('chat', routeUtil.chat); @@ -145,6 +146,8 @@ app.configure('production|development', function () { // filter configures app.filter(new pinus.filters.timeout()); + app.filter(checkEventFilter(app)); + // RPC 启用TCP协议 app.set('proxyConfig', { mailboxFactory: createTcpMailBox, diff --git a/game-server/app/servers/battle/filter/checkEventFilter.ts b/game-server/app/servers/battle/filter/checkEventFilter.ts new file mode 100644 index 000000000..f10378054 --- /dev/null +++ b/game-server/app/servers/battle/filter/checkEventFilter.ts @@ -0,0 +1,24 @@ +import {Application, RouteRecord, FrontendOrBackendSession, HandlerCallback} from "pinus"; +import {checkEvent} from '../handler/eventBattleHandler'; + +module.exports = function(app: Application) { + return new Filter(app); +} + +var Filter = function(this: any, app: Application) { + this.app = app; +}; + +Filter.prototype.before = function (routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, next: HandlerCallback) { + + next(null); +}; + +Filter.prototype.after = function (err: Error, routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, resp: any, next: HandlerCallback) { + return checkEvent(this.app, session).then(()=>{ + next(err); + }).catch(e => { + console.error(e); + next(err); + }); // 奇遇本检查 +}; \ No newline at end of file diff --git a/game-server/app/servers/battle/handler/battleUtils.ts b/game-server/app/servers/battle/handler/battleUtils.ts index 6b57de399..ec0f5f959 100644 --- a/game-server/app/servers/battle/handler/battleUtils.ts +++ b/game-server/app/servers/battle/handler/battleUtils.ts @@ -71,14 +71,14 @@ export class WarReward { private handleFixReward(num: number) { if(this.isSuccess) { // 成功了才给固定奖励 - let {fixReward} = this.warInfo; + let {fixReward = ''} = this.warInfo; let reward = decodeStr('fixReward', fixReward); for(let obj of reward) this.rewards.push({type: BATTLE_REWARD_TYPE.FIX_REWARD, ...obj, count: obj.count * num}); } } private handleConditionReward(num: number) { if(this.isSuccess) { - let {conditionReward} = this.warInfo; + let {conditionReward = ''} = this.warInfo; let reward = decodeStr('conditionReward', conditionReward); for(let obj of reward) { if(this.condition.get(obj.condition)) { @@ -90,7 +90,8 @@ export class WarReward { private async handleRandomReward(num: number) { if(this.isSuccess) { - let {randomReward} = this.warInfo; + let {RandomReward:randomReward = ''} = this.warInfo; + let reward = decodeStr('randomReward', randomReward); for(let obj of reward) { let { gid, frequency } = obj; diff --git a/game-server/app/servers/battle/handler/eventBattleHandler.ts b/game-server/app/servers/battle/handler/eventBattleHandler.ts new file mode 100644 index 000000000..ceb211bd6 --- /dev/null +++ b/game-server/app/servers/battle/handler/eventBattleHandler.ts @@ -0,0 +1,153 @@ +import { Application, BackendSession, FrontendOrBackendSession } from 'pinus'; +import { getGamedata } from '../../../util/gamedata'; +import { EventRecordModel } from '../../../db/EventRecord'; +import { RoleModel } from '../../../db/Role'; +import { genCode } from '../../../util/util'; + +export default function(app: Application) { + return new EventBattleHandler(app); +} + +export class EventBattleHandler { + constructor(private app: Application) { + } + + // 获取关卡列表 + async receiveEventReward(msg: { eventCode: string, isSuccess: boolean }, session: BackendSession) { + + } +} + +export async function setBattleStatus(roleId: string, battleId: number , isSuccess: boolean, battleCode: string, eventStatus: number = 0) { + let now = new Date(); + let refTime = eventStatus == 2? getEventTime(now): 0; + + let result = await EventRecordModel.setBattleStatus(roleId, battleId, refTime, isSuccess?1:0, battleCode); + if(eventStatus == 1) { + await RoleModel.setEventStatus(roleId, 2); + } + return result; +} + +export function getEventTime(now: Date) { + let curTime = Number(now); + let todayA = now.setHours(12, 0, 0, 0); // 每天12点 + let todayB = now.setHours(18, 0, 0, 0); // 每天18点 + let yesterdayA = todayA - 86400000; // 前一天12点 + let t = 0; + if(curTime < todayA) { + t = yesterdayA; + } else if (curTime >= todayA && curTime < todayB) { + t = todayA; + } else if (curTime >= todayB) { + t = todayB; + } + return t +} + +export async function startEvent(app: Application, session: FrontendOrBackendSession) { + + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + let channelName = roleId; + let event = await refreshEvent(1, roleId, roleName, 0); + await RoleModel.setEventStatus(roleId, 1); + session.set('eventStatus', 1); + session.push('eventStatus', () => {}); + // console.log('*******setEventStatus') + pushEventMsg(app, roleId, channelName, { event }); // 推送 + +} + +export async function checkEvent(app: Application, session: FrontendOrBackendSession) { + + try { + + let roleId = session.get('roleId'); + if(roleId) { + + let roleName = session.get('roleName'); + let channelName = roleId; + let eventStatus = session.get('eventStatus')||0; + + let eventTime = session.get('getEventTime')||0; + let now = new Date(); + let t = getEventTime(now); + + + let channel = app.get('channelService').getChannel(channelName, false); + console.log('****channel', channelName, !!channel, eventTime, t, eventStatus) + + if(!!channel && eventTime < t) { // 第一次登陆后可以刷新了 + session.set('getEventTime', t); + session.push('getEventTime', () => {}); + + if (eventStatus == 1) { + let event = await EventRecordModel.getEventRecordByTime(roleId, 0); + pushEventMsg(app, roleId, channelName, { event }); // 推送 + } else if( eventStatus == 2 ) { + + let event = await EventRecordModel.getEventRecordByTime(roleId, t); + if(event.length == 0) { // 刷新 + const num = 3; // 每次刷3个 + event = await refreshEvent(num, roleId, roleName, t); + } + + console.log(event) + // 推送 + pushEventMsg(app, roleId, channelName, { event }); + } + } + } + return; + }catch(err) { + console.log(err.stack); + throw err + } +} + +async function refreshEvent(num: number, roleId: string, roleName: string, t) { + let event = new Array(); + let dicEvent = getGamedata('dic_zyz_event'); + let historyRecord = await EventRecordModel.getHostoryEventRecord(roleId); + let {history, turn} = historyRecord; + let randomList = dicEvent.filter(cur => { + return history.find(ccur => { + return ccur.eventId == cur.eventID; + }); + }); + + for(let i = 0; i < num; i++) { + if(randomList.length == 0) { // 刷过新的一轮了 + turn ++; + randomList = [...dicEvent]; + } + + let index = Math.floor(Math.random() * randomList.length); + let dic = randomList[index]; + let eventCode = genCode(8); + let data = await EventRecordModel.saveEventRecord(eventCode, { + roleId, refTime: t, eventId: dic.eventID, + roleName, turn, type: dic.eventType, battleId: dic.warId||0, quality: dic.quality + }); + event.push(data) + randomList.splice(index, 1); + } + return event; +} + +function pushEventMsg(app, roleId, channelName, msg ) { + console.log('***pushEventMsg', channelName) + let channelService = app.get('channelService'); + + let param = { msg }; + let channel = channelService.getChannel(channelName, false); + if(!!channel) { + let tsid = channel.getMember(roleId)['sid']; + + channelService.pushMessageByUids('onSpecialEvent', param, [{ + uid: roleId, + sid: tsid + }]); + } +} \ No newline at end of file diff --git a/game-server/app/servers/battle/handler/normalBattleHandler.ts b/game-server/app/servers/battle/handler/normalBattleHandler.ts index 041f1bc07..0af111343 100644 --- a/game-server/app/servers/battle/handler/normalBattleHandler.ts +++ b/game-server/app/servers/battle/handler/normalBattleHandler.ts @@ -1,11 +1,13 @@ import { Application, BackendSession } from 'pinus'; import { BattleRecordModel } from '../../../db/BattleRecord'; import { BattleSweepRecordModel } from '../../../db/BattleSweepRecord'; -import { getWarById, getGoodById } from '../../../util/gamedata'; +import { getWarById, } from '../../../util/gamedata'; import { genCode } from '../../../util/util'; import { getAp, setAp, WarReward } from './battleUtils'; -import { WAR_TYPE } from '../../../consts/consts'; +import { WAR_TYPE, EVENT_START_BATTLE } from '../../../consts/consts'; import { checkDaily, checkDailyAndIncrease } from './dailyBattleHandler'; +import { setBattleStatus, startEvent } from './eventBattleHandler'; +import { queryMethod } from '@typegoose/typegoose'; export default function(app: Application) { return new NormalBattleHandler(app); @@ -129,6 +131,9 @@ export class NormalBattleHandler { return {code: 202, data: checkResult.msg} } dailyNum = { type: checkResult.type, count: checkResult.count, sum: checkResult.sum }; + } else if (warInfo.war_type == WAR_TYPE.EVENT) { + // 记录事件状态 + await setBattleStatus(roleId, battleId, isSuccess, battleCode, session.get('eventStatus')); } let warReward = new WarReward(roleId, roleName, battleId, isSuccess); @@ -165,6 +170,12 @@ export class NormalBattleHandler { const updateResult = await BattleRecordModel.updateBattleRecordByCode(battleCode, params, true); let { status } = updateResult; + // 主线关卡某个关卡触发事件开启 + let eventStatus = session.get('eventStatus')||0; + if(battleId == EVENT_START_BATTLE && eventStatus == 0) { + // console.log('*******startEvent') + await startEvent(this.app, session); + } return { code: 200, data: { diff --git a/game-server/app/servers/battle/remote/eventBattleRemote.ts b/game-server/app/servers/battle/remote/eventBattleRemote.ts new file mode 100644 index 000000000..0dfc3cb53 --- /dev/null +++ b/game-server/app/servers/battle/remote/eventBattleRemote.ts @@ -0,0 +1,72 @@ +import { Application, ChannelService, FrontendSession, RemoterClass } from 'pinus'; + +export default function (app: Application) { + return new EventBattleRemote(app); +} + +export class EventBattleRemote { + + constructor(private app: Application) { + this.app = app; + this.channelService = app.get('channelService'); + } + + private channelService: ChannelService; + + /** + * Add user into chat channel. + * + * @param {String} uid unique id for user + * @param {String} sid server id + * @param {boolean} flag channel parameter + * + */ + public async add(uid: string, sid: string, flag: boolean) { + let name = uid; + console.log('EventBattleRemote add: ', name, flag); + let channel = this.channelService.getChannel(name, flag); + if (!!channel && !this.get(name, false).includes(uid)) { + if (!!channel) { + channel.add(uid, sid); + } + } + return this.get(name, flag); + } + + /** + * Get user from chat channel. + * + * @param {Object} opts parameters for request + * @param {String} name channel name + * @param {boolean} flag channel parameter + * @return {Array} users uids in channel + * + */ + private get(name: string, flag: boolean) { + let users: string[] = []; + let channel = this.channelService.getChannel(name, flag); + if (!!channel) { + users = channel.getMembers(); + } + for (let i = 0; i < users.length; i++) { + users[i] = users[i].split('*')[0]; + } + return users; + } + + /** + * Kick user out chat channel. + * + * @param {String} uid unique id for user + * @param {String} sid server id + * + */ + public async kick(uid: string, sid: string) { + let name = uid; + let channel = this.channelService.getChannel(name, false); + // leave channel + if (!!channel) { + channel.leave(uid, sid); + } + } +} \ No newline at end of file diff --git a/game-server/app/servers/chat/handler/chatHandler.ts b/game-server/app/servers/chat/handler/chatHandler.ts index 4f92d5f1a..3835584ab 100644 --- a/game-server/app/servers/chat/handler/chatHandler.ts +++ b/game-server/app/servers/chat/handler/chatHandler.ts @@ -10,6 +10,9 @@ export class ChatHandler { constructor(private app: Application) { } + async test(msg: {content: string , target: string}, session: BackendSession) { + console.log('test') + } /** * Send messages to users * diff --git a/game-server/app/servers/connector/handler/entryHandler.ts b/game-server/app/servers/connector/handler/entryHandler.ts index ebde78741..f752a6799 100644 --- a/game-server/app/servers/connector/handler/entryHandler.ts +++ b/game-server/app/servers/connector/handler/entryHandler.ts @@ -58,10 +58,12 @@ export class EntryHandler { session.set('uid', role.roleId); session.set('roleId', role.roleId); session.set('roleName', role.roleName); + session.set('eventStatus', role.eventStatus); session.set('sid', self.app.get('serverId')); session.push('sid', () => {}); session.push('roleId', () => {}); session.push('roleName', () => {}); + session.push('eventStatus', () => {}); session.push('rid', function (err) { if (err) { console.error('set rid for session service failed! error is : %j', err.stack); @@ -69,7 +71,14 @@ export class EntryHandler { }); session.on('closed', this.onUserLeave.bind(this)); + let channelService = self.app.get('channelService'); + let channel = channelService.getChannel(role.roleId, true); + if (channel.getMembers().indexOf(role.roleId) === -1) { + channel.add(role.roleId, self.app.get('serverId')); + } + // put user into channel + await self.app.rpc.battle.eventBattleRemote.add.route(session)(role.roleId, self.app.get('serverId'), true); let users = await self.app.rpc.chat.chatRemote.add.route(session)(role.roleId, self.app.get('serverId'), rid, true); let heros = await HeroModel.findByRole(role.roleId); let equips = await EquipModel.findbyRole(role.roleId); @@ -93,6 +102,15 @@ export class EntryHandler { if (!session || !session.uid) { return; } + + let roleId = session.get('roleId'); + let sid = session.get('sid'); + + let channelService = this.app.get('channelService'); + let channel = channelService.getChannel(roleId, true); + channel.leave(roleId, sid); + + this.app.rpc.battle.eventBattleRemote.kick.route(session)(session.uid, this.app.get('serverId')); this.app.rpc.chat.chatRemote.kick.route(session, true)(session.uid, this.app.get('serverId'), session.get('rid')); } } \ No newline at end of file diff --git a/game-server/app/servers/user.rpc.define.ts b/game-server/app/servers/user.rpc.define.ts index ea4abcad3..e76768390 100644 --- a/game-server/app/servers/user.rpc.define.ts +++ b/game-server/app/servers/user.rpc.define.ts @@ -5,6 +5,7 @@ import { FrontendSession, RemoterClass } from 'pinus'; import { ChatRemote } from './chat/remote/chatRemote'; import { ComBattleRemote } from './battle/remote/comBattleRemote'; +import { EventBattleRemote } from './battle/remote/eventBattleRemote'; declare global { interface UserRpc { @@ -13,6 +14,7 @@ declare global { }; battle: { comBattleRemote: RemoterClass; + eventBattleRemote: RemoterClass; }; } } \ No newline at end of file diff --git a/game-server/app/util/util.ts b/game-server/app/util/util.ts index b48362346..7971daa3a 100644 --- a/game-server/app/util/util.ts +++ b/game-server/app/util/util.ts @@ -11,26 +11,29 @@ } export function decodeStr(type, str) { - if(str == '&') str = ''; - return str.split('|').map(cur => { - let arr = cur.split('&'); - let result = {}; - switch (type) { - case 'fixReward': { - let [gid, count] = arr; - result = { gid: parseInt(gid), count: parseInt(count)}; - break; + if(str == '&') { + return [] + } else { + return str.split('|').map(cur => { + let arr = cur.split('&'); + let result = {}; + switch (type) { + case 'fixReward': { + let [gid, count] = arr; + result = { gid: parseInt(gid), count: parseInt(count)}; + break; + } + case 'conditionReward': { + let [gid, count, condition] = arr; + result = { gid: parseInt(gid), count: parseInt(count), condition: parseInt(condition) }; + break; + } + case 'randomReward': { + let [gid, count, frequency] = arr; + result = { gid: parseInt(gid), count: parseInt(count), frequency: parseInt(frequency) }; + } } - case 'conditionReward': { - let [gid, count, condition] = arr; - result = { gid: parseInt(gid), count: parseInt(count), condition: parseInt(condition) }; - break; - } - case 'randomReward': { - let [gid, count, frequency] = arr; - result = { gid: parseInt(gid), count: parseInt(count), frequency: parseInt(frequency) }; - } - } - return result; - }); + return result; + }); + }; } \ No newline at end of file diff --git a/shared/consts/consts.ts b/shared/consts/consts.ts index 8486dfdad..5a7ead3b2 100644 --- a/shared/consts/consts.ts +++ b/shared/consts/consts.ts @@ -32,5 +32,9 @@ export const GOOD_TYPE = { export const WAR_TYPE = { NORMAL: 1, - DAILY: 2 -}; \ No newline at end of file + DAILY: 2, + EVENT: 3, + TOWER: 4 +}; + +export const EVENT_START_BATTLE = 101; \ No newline at end of file diff --git a/shared/db/EventRecord.ts b/shared/db/EventRecord.ts new file mode 100644 index 000000000..bc505707f --- /dev/null +++ b/shared/db/EventRecord.ts @@ -0,0 +1,56 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop } from '@typegoose/typegoose'; + +/** + * 战斗记录接口 + */ +@index({ roleId: 1, type: 1 }) + +export default class EventRecord extends BaseModel { + @prop({ required: true }) + roleId: string; // 角色 id + @prop({ required: true }) + roleName: string; // 角色 名 + @prop({ required: true }) + eventCode: string; // 这次刷新出的事件的唯一标识 + @prop({ required: true }) + eventId: number; // 事件 id + @prop({ required: true }) + type: number; // 事件类型 + @prop({ required: true, default: 0 }) + status: number; // 状态 1-已达成 0-未达成 -1-失败 + @prop({ required: true, default: 0 }) + battleStatus: number; // 状态 1-成功 -1-失败 + @prop({ required: true, default: 0 }) + refTime: number; // 刷新时间 + @prop({ required: true, default: 0 }) + battleId: number; // 关卡类型,关卡id + @prop({ required: true, default: 0 }) + turn: number; // 随机的轮数,保证每个event_id都能随机到一次再进行下一轮 + + + public static async getEventRecordByTime(roleId: string, refTime: number, lean=true ) { + let data = await EventRecordModel.find({roleId, refTime}).select('eventCode eventId type quality status battleId').lean(lean); + return data; + } + + public static async getHostoryEventRecord(roleId: string, lean=true) { + let latest = await EventRecordModel.findOne({roleId}).sort({turn: -1}).lean(); + let {turn = 0} = latest||{}; + let result = await EventRecordModel.find({roleId, turn}).lean(lean); + return {history: result, turn}; + } + + public static async saveEventRecord(eventCode: string, params: {roleId: string, refTime: number, eventId: number, roleName:string, turn: number, type: number, quality: number, battleId?: number } , lean=true ) { + let data = await EventRecordModel.findOneAndUpdate({eventCode}, { $set: {...params, status: 0 } }, {upsert: true, new: true}).select('eventCode eventId type quality status battleId').lean(lean); + return data; + } + + public static async setBattleStatus(roleId: string, battleId: number, refTime: number, battleStatus: number, battleCode: string , lean=true ) { + let result = await EventRecordModel.findOneAndUpdate({roleId, battleId, refTime}, {$set:{ battleStatus, battleCode }}).sort({turn: 1}).lean(lean); + return result; + } + +} + +export const EventRecordModel = getModelForClass(EventRecord); diff --git a/shared/db/Role.ts b/shared/db/Role.ts index 2e81938cd..2c8a49964 100644 --- a/shared/db/Role.ts +++ b/shared/db/Role.ts @@ -94,6 +94,9 @@ export default class Role extends BaseModel { @prop({ required: true, default: 1 }) towerLv: number; // 天梯当前层数 + @prop({ required: true }) + eventStatus: number; // 奇遇开启状态, 0-未开启 1-开启了第一场事件 2-完全开启 + public static async findByUid(uid: number, serverId: number, lean = true) { const role = await RoleModel.findOne({ 'userInfo.uid': uid, serverId }).lean(lean); return role; @@ -124,6 +127,12 @@ export default class Role extends BaseModel { const user = await RoleModel.find(searchObj).lean(lean); return user; } + + + public static async setEventStatus(roleId: string, eventStatus: number, lean = true) { + const role = await RoleModel.findOneAndUpdate({ roleId }, { eventStatus }).lean(lean); + return role; + } } export const RoleModel = getModelForClass(Role); diff --git a/web-server/app/controller/home.ts b/web-server/app/controller/home.ts index e55186747..e305b99aa 100644 --- a/web-server/app/controller/home.ts +++ b/web-server/app/controller/home.ts @@ -8,6 +8,13 @@ export default class HomeController extends Controller { public async dev() { const { ctx } = this; + let time1 = new Date(); + let time2 = Number(time1); + let offset = time1.getTimezoneOffset(); + + let today = time1.setHours(0, 0, 0, 0); + + console.log(time1, time2, offset, today); await ctx.render('index',{ title: 'xxx' }); diff --git a/web-server/app/public/index.html b/web-server/app/public/index.html index e096f6c01..a3cb27887 100644 --- a/web-server/app/public/index.html +++ b/web-server/app/public/index.html @@ -60,8 +60,23 @@ +

+
+ +
+
+
+ + +
+
+ +
+
+

+ +
-
diff --git a/web-server/config/config.local.ts b/web-server/config/config.local.ts index 56415cecc..f565a51a2 100644 --- a/web-server/config/config.local.ts +++ b/web-server/config/config.local.ts @@ -1,6 +1,59 @@ -import { EggAppConfig, PowerPartial } from 'egg'; +import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg'; +const path = require('path'); -export default () => { - const config: PowerPartial = {}; - return config; +export default (appInfo: EggAppInfo) => { + const config = {} as PowerPartial; + + // override config from framework / plugin + // use for cookie sign key, should change to your own and keep security + config.keys = appInfo.name + '_1597499383757_3508'; + config.security = { + csrf: { + enable: false, + }, + domainWhiteList: [ '*' ], + }; + config.cors = { + origin: '*', // 匹配规则 域名+端口 *则为全匹配 + allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', + }; + // add your egg config in here + config.middleware = [ 'parmsDecode' ]; + + config.mongoose = { + url: 'mongodb://127.0.0.1/admin', // 内网 + options: { useNewUrlParser: true, useUnifiedTopology: true }, + }; + + config.alinode = { + appid: '86043', + secret: '54ef0364995b0c4f2ab42150e29ad30df8327a3a', + error_log: [ '/root/logs/zyz/zyz-web.log', '/root/logs/zyz/common-error.log', '/root/logs/zyz/egg-agent.log' ], + packages: [ '/root/zyz/web-server/package.json' ], + }; + + config.view = { + root: path.join(appInfo.baseDir, '/app/public'), + defaultViewEngine: 'nunjucks', + mapping: { + '.html': 'nunjucks' //左边写成.html后缀,会自动渲染.html文件 + }, + }; + + config.static = { + prefix: '/', + dir: path.join(appInfo.baseDir, '/app/public'), + }; + + + // add your special config in here + const bizConfig = { + sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`, + }; + + // the return config will combines to EggAppConfig + return { + ...config, + ...bizConfig, + }; }; diff --git a/web-server/package.json b/web-server/package.json index d4b690dcf..e948e8589 100644 --- a/web-server/package.json +++ b/web-server/package.json @@ -19,7 +19,8 @@ "ci": "npm run lint && npm run cov && npm run tsc", "autod": "autod", "lint": "eslint . --ext .ts", - "clean": "ets clean" + "clean": "ets clean", + "local": "EGG_SERVER_ENV=local npm run dev" }, "dependencies": { "csprng": "^0.1.2",