根据 chat 示例创建 game-server,支持分布式部署、域名访问、数据库连接和基础使用

This commit is contained in:
liangtongchuan
2020-08-15 20:34:31 +08:00
parent e52a829567
commit 8ce0dc040f
36 changed files with 3165 additions and 2 deletions

View File

@@ -0,0 +1,24 @@
import { prop, pre } from '@typegoose/typegoose';
/**
* BaseModel
*/
@pre<BaseModel>('save', function (next) {
if (!this.createdAt || this.isNew) {
this.createdAt = this.updatedAt = new Date()
} else {
this.updatedAt = new Date()
}
next()
})
export default class BaseModel {
_id?: string
@prop()
createdAt: Date
@prop()
updatedAt: Date
}

View File

@@ -0,0 +1,45 @@
import BaseModel from './BaseModel';
import { index, getModelForClass, prop } from '@typegoose/typegoose';
/**
* 用户字段接口
*/
@index({ userNo: 1 })
export default class User extends BaseModel {
@prop({ required: true})
userNo: number;
@prop({ required: true})
userName: string;
@prop({ required: true})
token: string;
@prop({ required: true})
telHash: string;
//#region实例方法 和 实例方法)
public async userInstanceTestMethods() {
const user: User = new User();
user.userName = '我是实例化方法测试';
user.userNo = 9527;
return user;
}
public static async userStaticTestMethods() {
const user: User = new User();
user.userName = '我是静态方法测试';
user.userNo = 9527;
return user;
}
//#endregion
}
export const UserModel = getModelForClass(User);

View File

@@ -0,0 +1,76 @@
import { UserModel } from './../../../db/User';
import { ChatRemote } from '../remote/chatRemote';
import {Application, BackendSession} from 'pinus';
import { FrontendSession } from 'pinus';
export default function(app: Application) {
return new ChatHandler(app);
}
export class ChatHandler {
constructor(private app: Application) {
}
/**
* Send messages to users
*
* @param {Object} msg message from client
* @param {Object} session
*
*/
async send(msg: {content: string , target: string}, session: BackendSession) {
let rid = session.get('rid');
let username = session.uid.split('*')[0];
let channelService = this.app.get('channelService');
let param = {
msg: msg.content,
from: username,
target: msg.target
};
let channel = channelService.getChannel(rid, false);
// the target is all users
if (msg.target === '*') {
channel.pushMessage('onChat', param);
}
// the target is specific user
else {
let tuid = msg.target + '*' + rid;
let tsid = channel.getMember(tuid)['sid'];
channelService.pushMessageByUids('onChat', param, [{
uid: tuid,
sid: tsid
}]);
}
}
async send2(msg: {content: string , target: string}, session: BackendSession) {
let rid = session.get('rid');
let username = session.uid.split('*')[0];
let channelService = this.app.get('channelService');
let param = {
msg: msg.content,
from: username,
target: msg.target
};
let channel = channelService.getChannel(rid, false);
console.log(`got user in send2 :`,);
const user = await UserModel.findOneAndUpdate({userName: username}, {userName: username, userNo: 666}, {upsert: true, new: true}).lean();
console.log(`got user in send2 :`, user);
// the target is all users
if (msg.target === '*') {
channel.pushMessage('onChat', param);
}
// the target is specific user
else {
let tuid = msg.target + '*' + rid;
let tsid = channel.getMember(tuid)['sid'];
channelService.pushMessageByUids('onChat', param, [{
uid: tuid,
sid: tsid
}]);
}
}
}

View File

@@ -0,0 +1,92 @@
import { Application, ChannelService, FrontendSession, RemoterClass } from 'pinus';
export default function (app: Application) {
return new ChatRemote(app);
}
// rpc 定义挪到单独的定义文件(user.rpc.define.ts)。解决ts-node 有可能找不到定义的问题。
// 你也可以用其它方法解决,或者没有遇到过这个问题的话,定义还是可以放在这里。
// UserRpc的命名空间自动合并
// declare global {
// interface UserRpc {
// chat: {
// chatRemote: RemoterClass<FrontendSession, ChatRemote>;
// };
// }
// }
export class ChatRemote {
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 {String} name channel name
* @param {boolean} flag channel parameter
*
*/
public async add(uid: string, sid: string, name: string, flag: boolean) {
let channel = this.channelService.getChannel(name, flag);
let username = uid.split('*')[0];
let param = {
user: username
};
channel.pushMessage('onAdd', param);
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
* @param {String} name channel name
*
*/
public async kick(uid: string, sid: string, name: string) {
let channel = this.channelService.getChannel(name, false);
// leave channel
if (!!channel) {
channel.leave(uid, sid);
}
let username = uid.split('*')[0];
let param = {
user: username
};
channel.pushMessage('onLeave', param);
}
}

View File

@@ -0,0 +1,63 @@
import {Application} from 'pinus';
import {FrontendSession} from 'pinus';
export default function (app: Application) {
return new EntryHandler(app);
}
export class EntryHandler {
constructor(private app: Application) {
}
/**
* New client entry chat server.
*
* @param {Object} msg request message
* @param {Object} session current session object
*/
async enter(msg: { rid: string, username: string }, session: FrontendSession) {
let self = this;
let rid = msg.rid;
let uid = msg.username + '*' + rid;
let sessionService = self.app.get('sessionService');
// duplicate log in
if (!!sessionService.getByUid(uid)) {
return {
code: 500,
error: true
};
}
await session.abind(uid);
session.set('rid', rid);
session.push('rid', function (err) {
if (err) {
console.error('set rid for session service failed! error is : %j', err.stack);
}
});
session.on('closed', this.onUserLeave.bind(this));
// put user into channel
let users = await self.app.rpc.chat.chatRemote.add.route(session)(uid, self.app.get('serverId'), rid, true);
return {
users: users
};
}
/**
* User log out handler
*
* @param {Object} app current application
* @param {Object} session current session object
*
*/
onUserLeave(session: FrontendSession) {
if (!session || !session.uid) {
return;
}
this.app.rpc.chat.chatRemote.kick.route(session, true)(session.uid, this.app.get('serverId'), session.get('rid'));
}
}

View File

@@ -0,0 +1,42 @@
import { dispatch } from '../../../util/dispatcher';
import { Application , BackendSession} from 'pinus';
export default function (app: Application) {
return new GateHandler(app);
}
export class GateHandler {
constructor(private app: Application) {
}
/**
* Gate handler that dispatch user to connectors.
*
* @param {Object} msg message from client
* @param {Object} session
*
*/
async queryEntry(msg: {uid: string}, session: BackendSession) {
let uid = msg.uid;
if (!uid) {
return {
code: 500
};
}
// get all connectors
let connectors = this.app.getServersByType('connector');
console.log('ltc connectors', connectors);
if (!connectors || connectors.length === 0) {
return {
code: 500
};
}
// select connector
let res = dispatch(uid, connectors);
return {
code: 200,
host: res.host,
port: res.clientPort
};
}
}

View File

@@ -0,0 +1,14 @@
// 这种
// UserRpc的命名空间自动合并
import { FrontendSession, RemoterClass } from 'pinus';
import { ChatRemote } from './chat/remote/chatRemote';
declare global {
interface UserRpc {
chat: {
chatRemote: RemoterClass<FrontendSession, ChatRemote>;
};
}
}

View File

@@ -0,0 +1,7 @@
import * as crc from 'crc';
import { ServerInfo } from 'pinus';
export function dispatch(uid: string , connectors: ServerInfo[]) {
let index = Math.abs(crc.crc32(uid)) % connectors.length;
return connectors[index];
}

View File

@@ -0,0 +1,15 @@
import { dispatch} from './dispatcher';
import { Session, Application } from 'pinus';
export function chat(session: Session, msg: any, app: Application, cb: (err: Error , serverId ?: string) => void) {
let chatServers = app.getServersByType('chat');
if(!chatServers || chatServers.length === 0) {
cb(new Error('can not find chat servers.'));
return;
}
let res = dispatch(session.get('rid'), chatServers);
cb(null, res.id);
}