Files
ZYZ/web-server/app/controller/game.ts
2023-07-31 16:42:25 +08:00

348 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { UserModel } from '@db/User';
import { LadderMatchRecModel } from '@db/LadderMatchRec';
import { PvpRecordModel } from '@db/PvpRecord';
import { BattleRecordModel } from '@db/BattleRecord';
import { STATUS, WAR_TYPE } from '@consts';
import { Controller } from 'egg';
import * as fs from 'fs';
import { RoleModel } from '@db/Role';
import { NoticeModel } from '@db/Notice';
import { ServerParamWithRole, GroupParam } from '../domain/gameField/serverlist';
import { reloadResources } from 'app/pubUtils/data';
import { ServerlistModel } from '@db/Serverlist';
import { dispatch } from 'app/pubUtils/dispatcher';
import { RedisClient } from 'redis';
import { REDIS_KEY } from '@consts';
import { RegionModel } from '@db/Region';
import { getRandEelmWithWeight } from 'app/pubUtils/util';
import { getLocalRplUrl, getRemoteRplUrl, getRemoteRplPrefix } from 'app/pubUtils/battleUtils'
import { ChannelInfoModel } from '@db/ChannelInfo';
import { GVGVestigeRecModel } from '@db/GVGVestigeRec';
import { GVGBattleRecModel } from '@db/GVGBattleRec';
import { PackageModel } from '@db/Package';
import { nowSeconds } from 'app/pubUtils/timeUtil';
const sendToWormhole = require('stream-wormhole');
const pump = require('mz-modules/pump');
export default class GameController extends Controller {
public async checkVersion() {
const { ctx } = this;
const { version } = ctx.request.body;
let curRegion = await RegionModel.findRegionByEnv(this.app.config.realEnv);
if(!curRegion) {
return ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR);
}
const versionFlag = ctx.service.utils.compareVersion(version, curRegion.minVersion);
if (versionFlag >= 0) {
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS);
return;
}
//版本号太低
ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR, { version: curRegion.minVersion });
return;
}
public async checkReview() {
const { ctx } = this;
const { version, platformAppid, platformAppId, addressType, platform: clientPlatform } = ctx.request.body;
let curRegion = await RegionModel.findRegionByEnv(this.app.config.realEnv);
if(!curRegion) {
return ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR);
}
if(curRegion.addressType != addressType) {
return ctx.body = ctx.service.utils.resResult(STATUS.ADDRESS_ERR);
}
let hasPolicy = false, userPolicyLink = '', privacyPolicyLink = ''; // 是否需要替换协议,有就是用下面两个字段替换
let platform = platformAppid ?? platformAppId;
let channelInfo = await ChannelInfoModel.findByPlatform(platform);
if(channelInfo && !channelInfo.isDefaultPolicy) {
hasPolicy = true;
userPolicyLink = channelInfo.userPolicyLink;
privacyPolicyLink = channelInfo.privacyPolicyLink;
}
let isReview = false;
if (platform === 'ios' || clientPlatform === 'ios') {
isReview = await ctx.service.update.checkReview(curRegion, version);
}
let hasNewWebServer = false, webServerUrl = '';
if(isReview && curRegion.reviewEnv) {
let reviewRegion = await RegionModel.findRegionByEnv(curRegion.reviewEnv);
if(reviewRegion) {
hasNewWebServer = true, webServerUrl = reviewRegion.webHost;
}
}
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isReview, hasNewWebServer, webServerUrl, hasPolicy, userPolicyLink, privacyPolicyLink });
return;
}
public async getServerList() {
try {
const { ctx } = this;
let { uid } = ctx;
const { pid, gid, version } = ctx.request.body;
let serverList = new Array<GroupParam>();
let loginServerList = new Array<ServerParamWithRole>();
let curRegion = await RegionModel.findRegionByEnv(this.app.config.realEnv);
if(!curRegion) {
return ctx.body = ctx.service.utils.resResult(STATUS.VERSION_ERR);
}
// if(curRegion.addressType != addressType) {
// return ctx.body = ctx.service.utils.resResult(STATUS.ADDRESS_ERR);
// }
// let isReview = await ctx.service.update.checkReview(curRegion, version);
// let env = isReview? curRegion.reviewEnv: ctx.app.config.realEnv;
let allServers = await ServerlistModel.findByEnv(ctx.app.config.realEnv, false);
let hasRole = await RoleModel.checkHasRole(uid, curRegion.latestServerUniqId);
if(pid && gid) {
const pkg = await PackageModel.getPackageByGidPid(gid, pid);
if(pkg && pkg.hideServers && pkg.hideServers.length > 0) {
allServers = allServers.filter(server => !pkg.hideServers.includes(server.id));
}
}
// 新开服的展示 需要提前12小时超过12小时不在前端展示即将开启
// 新开服的数量有且仅显示一个目的为了防止1天多个服导致出来多个即将开启
let hideFutureServerIds: number[] = [], showFutureServerIds: number[] = [];
for(let server of allServers) {
if(server.openTime > nowSeconds() + 12 * 60 * 60) hideFutureServerIds.push(server.id);
if(server.openTime > nowSeconds() && server.openTime <= nowSeconds() + 12 * 60 * 60) showFutureServerIds.push(server.id);
}
if(showFutureServerIds.length > 0) {
showFutureServerIds.sort((a, b) => b - a);
hideFutureServerIds.push(...showFutureServerIds.slice(0, showFutureServerIds.length -1))
}
if(hideFutureServerIds.length > 0) {
allServers = allServers.filter(server => !hideFutureServerIds.includes(server.id));
}
let roles = await RoleModel.findAllByUid(uid, true, true);
for (let server of allServers) {
let status = ctx.service.utils.getServerStatus(server, version, hasRole)
let curGroup = serverList.find(cur => cur.groupId == server.groupId);
if (!curGroup) {
curGroup = new GroupParam(server);
serverList.push(curGroup);
}
curGroup.pushServer(server, status);
let role = roles.find(role => role.serverId == server.id);
if (!!role) {
let curLoginInfo = new ServerParamWithRole(role, server, status);
loginServerList.push(curLoginInfo);
}
}
loginServerList.sort((a, b) => { return b.updatedAt.getTime() - a.updatedAt.getTime() });
if (serverList) {
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { serverList, loginServerList });
} else {
ctx.body = ctx.service.utils.resResult(STATUS.SERVER_NOT_FOUND);
}
return
}catch(e) {
console.error(e);
}
}
public async getnotice() {
const { ctx } = this;
const { pid, gid } = ctx.request.body;
let packageCode = 'default';
if(pid && gid) {
let pkg = await PackageModel.getPackageByGidPid(gid, pid);
if(pkg) packageCode = pkg?.packageCode;
}
let notice = await NoticeModel.getAllNotice(packageCode);
if(notice.length == 0) notice = await NoticeModel.getAllNotice('default');
let result = notice.map(cur => {
let { id, title, tag, type, content, time } = cur;
return {
id, title, tag, type, content, time
}
})
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { notice: result });
return
}
public async reloadResource() {
const { ctx } = this;
try {
reloadResources();
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true });
return;
} catch (e) {
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: (<Error>e).stack });
return;
}
}
public async queryEnter() {
const { ctx } = this;
const { app, userCode } = ctx;
let redisClient: RedisClient = app.context.redisClient;
let hash = await redisClient.hvalsAsync(REDIS_KEY.SYS_SERVER);
let connectors = hash.map(cur => JSON.parse(cur));
if (!connectors || connectors.length === 0) {
ctx.body = ctx.service.utils.resResult(STATUS.CONNECTOR_ERR);
return
}
// select connector
let sum = connectors.reduce((pre, cur) => pre + (cur['num']||0), 0);
let res;
if(sum > 0) {
let serversWithWeight = connectors.map(cur => ({...cur, weight: sum - (cur['num']||0)}));
let randResult = getRandEelmWithWeight(serversWithWeight);
res = randResult.dic;
}
if(!res) {
res = await dispatch(ctx.app.context.redisClient, userCode, connectors, 'connector');
}
let { id, serverType, clientHost, clientPort, num = 0 } = res;
await redisClient.hsetAsync(REDIS_KEY.SYS_SERVER, id, JSON.stringify({ serverType, clientHost, clientPort, id, num: num + 1 }));
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { host: res.clientHost, port: res.clientPort });
return
}
public async upload() {
const { ctx } = this;
const parts = ctx.multipart();
let part;
let [writePath, token, battleCode, fullPath, remoteUrl, warType, prefix] = ['', '', '', '', '', 0, ''];
while ((part = await parts()) != null) {
if (part.length) {
console.log('kv: ', `${part[0]}: ${part[1]}`);
if (part[0] === 'token') {
token = part[1];
} else if (part[0] === 'battleCode') {
battleCode = part[1];
} else if (part[0] === 'warType') {
// 检测 part[1] 类型
if (typeof part[1] === 'number') {
warType = part[1];
} else if (typeof part[1] === 'string' && !isNaN(parseInt(part[1]))) {
warType = parseInt(part[1]);
}
}
} else {
if (!part.filename) {
continue;
}
console.log(part);
if (part.fieldname === 'rpl' && battleCode !== '') {
console.log('field: ', part.fieldname);
console.log('filename: ', part.filename);
if (token === '') {
ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS);
return;
}
const user = await UserModel.findUserByToken(token);
if (!user) {
console.error('token invalid');
ctx.body = ctx.service.utils.resResult(STATUS.TOKEN_ERR);
return;
}
let roleId = '';
if (!!warType && warType === WAR_TYPE.GVG_BATTLE) { // gvg激战期录像没有通用 BattleRecord单独处理
let gvgBattleRec = await GVGBattleRecModel.findByBattleCode(battleCode);
if (!gvgBattleRec) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_NOT_FOUND);
roleId = gvgBattleRec.attackTeam?.roleId;
if (!roleId) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_NOT_FOUND);
} else {
let battleRec = await BattleRecordModel.getBattleRecordByCode(battleCode, true);
if (!battleRec) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_NOT_FOUND);
roleId = battleRec.roleId;
warType = battleRec.warType;
// if (warType !== WAR_TYPE.PVP && warType !== WAR_TYPE.LADDER && warType !== WAR_TYPE.GVG_VESTIGE) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_RPL_NOT_SUPPORT);
}
writePath = getLocalRplUrl(roleId, warType, battleCode);
try {
fs.accessSync(writePath);
} catch (err) {
if (err) {
fs.mkdirSync(writePath, { recursive: true });
}
}
fullPath = `${writePath}/${battleCode}.bin`
console.log(fullPath);
if (!fs.existsSync(fullPath)) {
fs.writeFileSync(fullPath, '');
}
const writeStream = fs.createWriteStream(fullPath);
await pump(part, writeStream);
prefix = getRemoteRplPrefix(ctx.app.config.realEnv);
remoteUrl = `${getRemoteRplUrl(ctx.app.config.sshHost,roleId, warType, battleCode)}/${battleCode}.bin`;
let updateDBRes;
if (warType === WAR_TYPE.PVP) {
updateDBRes = await PvpRecordModel.updateRplStatus(battleCode, true, remoteUrl);
} else if (warType === WAR_TYPE.LADDER) {
updateDBRes = await LadderMatchRecModel.updateRplStatus(battleCode, true, remoteUrl);
} else if (warType == WAR_TYPE.GVG_VESTIGE) {
updateDBRes = await GVGVestigeRecModel.updateRplStatus(battleCode, true, remoteUrl);
} else if (warType == WAR_TYPE.GVG_BATTLE) {
updateDBRes = await GVGBattleRecModel.updateRplStatus(battleCode, true, remoteUrl);
} else {
updateDBRes = await BattleRecordModel.updateBattleRecordByCode(battleCode, { $set: { remoteUrl }});
}
if (!updateDBRes) return ctx.body = ctx.service.utils.resResult(STATUS.BATTLE_RPL_UPDATE_ERR);
} else {
await sendToWormhole(part);
}
}
}
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { rplUrl: prefix + remoteUrl });
return;
}
// 根据 pid 和 gid 获取大区地址
public async getRegionAddr() {
const { ctx } = this;
const { pid, gid } = ctx.request.body;
if (!pid || !gid) {
ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS);
return;
}
const pkg = await PackageModel.getPackageByGidPid(gid, pid);
if (!pkg) {
ctx.body = ctx.service.utils.resResult(STATUS.PACKAGE_NOT_FOUND);
return;
}
const { regionId, loginPage = '', loadingPage = '' } = pkg;
const region = await RegionModel.findRegionById(regionId);
if (!region) {
ctx.body = ctx.service.utils.resResult(STATUS.REGION_NOT_FOUND);
return;
}
const { webHost = 'https://sq7-web-sgzzyz.yev242.com', addressType = 9 } = region;
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { webHost, addressType, loginPage, loadingPage });
return;
}
}