1 Star 2 Fork 0

散漫的水元素 / game

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
server.js 11.02 KB
一键复制 编辑 原始数据 按行查看 历史
wangmiao 提交于 2021-06-19 13:02 . 优化网络性能
/**
* Created by wm123 on 2017/6/20.
*/
const server = require('server');
const extend = require('extend');
const Player = require('./player');
const Room = require('./room');
const Role = require('./role');
const Game = require('./game');
const Board = require('./board');
const Team = require('./team');
const Group = require('./group');
const Messages = require('./messages');
const Dispatcher = require('./dispatcher');
const utils = require('./utils');
const pack = require('./pack');
const { get, socket, error } = server.router;
const { file, status, send } = server.reply;
const path = require('path');
const seo = require('./seo');
const random = require('./random');
const execute = execute => data => {
try {
execute(data)
} catch (e) {
e && console.error(e);
data.socket.emit('err', e && (e.message || e));
}
};
const notEmpty = value => {
return Array.isArray(value) ? value.some(o => typeof o === 'object' ? notEmpty(o) : typeof o !== 'undefined') : (typeof value !== 'undefined' && Object.keys(value).length > 0);
}
const notSameArray = (value, old, diff) => {
if (Array.isArray(value) && Array.isArray(old) && Array.isArray(diff)) {
if (diff.every(o => typeof o !== 'undefined' && typeof o !== 'object')) {
return value.length !== old.length || value.some((o, index) => o !== old[index]);
} else {
return true;
}
} else {
return true;
}
}
const diff = (data, old) => {
let isArray = Array.isArray(data);
let result = (isArray ? [] : { });
old = typeof old === 'undefined' ? (isArray ? [] : { }) : old;
for (let name in data) {
if (data.hasOwnProperty(name)) {
let value = data[name];
if (typeof value === 'undefined') {
if (typeof old[name] !== 'undefined') {
if (isArray) {
result[name] = undefined;
} else {
result['~' + name] = true;
}
}
} else if (typeof value === 'object') {
if (typeof old[name] === 'undefined') {
result[name] = value;
} else {
let d = diff(value, old[name]);
if (isArray || notEmpty(d)) {
if (!Array.isArray(d) || notSameArray(value, old[name], d)) {
result[name] = d;
}
}
}
} else if (isArray || value !== old[name]) {
result[name] = value;
}
}
}
if (typeof data !== 'undefined') {
for (let name in old) {
if (old.hasOwnProperty(name)) {
if (!(name in data) && !name.startsWith('~')) {
result['~' + name] = true;
}
}
}
}
return result;
};
const emit = (socket, event, data, key) => {
if (key === false) {
socket.emit(event, data);
} else {
let emits = socket.emits || { };
let prop = typeof key !== 'undefined' ? event + '.' + data[key] : event;
let send = diff(data, emits[prop]);
if (notEmpty(send)) {
socket.emit(event, send);
emits[prop] = data;
socket.emits = emits;
}
}
};
const getRemoteAddress = socket => socket.client.conn.request.headers['x-real-ip'] || socket.client.conn.remoteAddress;
const proxy = socket => new Proxy({ }, {
get(target, property, receiver) {
if (property in target) {
return Reflect.get(target, property, receiver);
} else {
return data => emit(socket, property.replace(/^[_$]/i, ''), data, false);
}
}
});
const last = data => {
if (Array.isArray(data)) {
return [ data[data.length - 1] ];
} else {
if ('last' in data) {
let last = data.last;
if (typeof last === 'function') {
return last.call(data);
} else {
return last;
}
} else if ('data' in data) {
return last(data.data);
}
}
};
const apply = (execute, data, socket, server) => {
if (execute) {
if (Array.isArray(execute)) {
execute.forEach(exec => apply(exec, data, socket, server));
} else {
execute({
data, server, socket,
emit: proxy(socket),
player: socket.player,
room: socket.player && socket.player.room,
role: socket.player && socket.player.room && socket.player.role,
game: socket.player && socket.player.room && socket.player.room.game,
watch: (dispatcher, event, getter, key) => dispatcher && dispatcher.on(dispatcher => emit(socket, event, getter ? getter(dispatcher) : dispatcher.data, key), socket.player),
append: (dispatcher, event, getter) => dispatcher && dispatcher.on(dispatcher => {
let data = last(getter ? getter(dispatcher) : dispatcher);
if (data) emit(socket, event, data, false);
}, socket.player, true)
});
}
}
};
const watchRole = (watch, role) => watch(role, 'role');
const watchPlayer = (watch, player) => watch(player, 'player');
const watchRoom = (watch, room) => watch(room, 'room');
const watchGame = (watch, game) => watch(game, 'game', game => game.summary);
const watchGroups = (watch, game) => game && game.groups.forEach(group => watch(group, 'group', group => group.summary, 'index'));
const watchBoard = (watch, game) => game && watch(game.board, 'board');
const appendRoomMessage = (append, room) => room && append(room.messages, 'message');
const appendGameMessage = (append, game) => game && append(game.messages, 'message');
const defaultMappings = {
connection: () => { },
login: [
({ data, socket, server }) => socket.player = Player.login(server, data.id, data.name || '', data.password),
({ watch, append, player, role, room, game }) => {
watchPlayer(watch, player);
watchRole(watch, role);
watchRoom(watch, room);
watchGame(watch, game);
watchGroups(watch, game);
watchBoard(watch, game);
appendRoomMessage(append, room);
appendGameMessage(append, game);
}
],
info: ({ data, socket, server }) => socket.player = Player.get(server, data.id, data.name || ''),
rooms: ({ data, emit, server }) => {
let page = data && data.page || 0;
emit.rooms({ page, list: Room.list(server, page).map(room => room.summary) });
},
settings: ({ emit, server }) => { emit.settings(extend(true, { publish: true }, server.config.room)) },
create: [
({ data, player }) => player.createRoom(data),
({ watch, append, role, room }) => {
watchRole(watch, role);
watchRoom(watch, room);
appendRoomMessage(append, room);
}
],
join: [
({ data, player }) => player.joinRoom(data.roomId),
({ watch, append, role, room }) => {
watchRole(watch, role);
watchRoom(watch, room);
appendRoomMessage(append, room);
}
],
exit: ({ role }) => role.exit(),
move: ({ data, role }) => role.move(data),
disable: ({ data, role }) => role.disable(data),
enable: ({ data, role }) => role.enable(data),
kick: ({ data, role }) => role.kick(data),
start: ({ room, role }) => room.start(role),
ready: [
({ role }) => role.ready(),
({ watch, append, game }) => {
watchGame(watch, game);
watchGroups(watch, game);
watchBoard(watch, game);
appendGameMessage(append, game);
}
],
confirm: ({ role }) => role.confirm(),
message: ({ role, data }) => role.message(data),
disconnect: ({ socket }) => {
socket.player && socket.player.leave();
socket.emits = { };
}
};
class GameServer {
/**
*
* @param mappings a list of functions, each function accept one param: <object> context.
* context.role: role in session
* context.data: the data received from client
* context.room: room of role in session
* context.server: the server object
* context.socket: the socket object which is connect to client
* context.game: game of role in session
* @param config
*/
constructor(mappings, ...config) {
this.mappings = mappings;
this.config = extend(true, { }, {
Player, Room, Role, Game, Board, Team, Group,
assets: './assets',
compress: true,
watch: false,
devtool: 'source-map',
stats: 'error-only',
presets: [
require.resolve('babel-preset-es2015'),
require.resolve('babel-preset-stage-3')
],
mvvm: 'vue',
css: 'less',
random: random.car
}, {
teams: [
{
color: 'white'
}
],
team: {
size: 5,
min: 2,
regroup: false
}
}, ...config);
this.players = new Map();
this.rooms = new Map();
this.roomIds = [];
}
start() {
pack(this.config, () => {
if (!this.server) {
this.server = server({ port: this.config.port, 'public': this.config.assets, views: path.join(process.cwd(), this.config.assets), index: false },
[
get('/', ctx => {
return seo.need(ctx.req) ? file(path.join(this.config.assets, 'seo.html')) : file(path.join(this.config.assets, 'entry.html'));
}),
get('/id', ctx => ctx.req.session.id),
get('/random', async ctx => send(await this.config.random(ctx)))
]
.concat(Object.keys(defaultMappings).map(key => {
socket(key, execute(({ data, socket }) => {
apply(defaultMappings[key], data, socket, this);
apply(this.mappings[key], data, socket, this);
}))
}))
.concat(Object.keys(this.mappings).filter(key => !(key in defaultMappings)).map(key => socket(key, execute(({ data, socket }) => apply(this.mappings[key], data, socket, this)))))
.concat([
error(ctx => status(500).send(ctx.error.message))
]));
console.log(`${ this.config.name || '' }服务器在端口${ this.config.port }上已启动`);
}
})
}
static start(mappings, ...config) {
return new GameServer(mappings, ...config).start();
}
}
module.exports = {
GameServer, Player, Room, Role, Game, Board, Team, Group, Messages, Dispatcher, utils, random
};
JavaScript
1
https://gitee.com/wm123450405/game.git
git@gitee.com:wm123450405/game.git
wm123450405
game
game
master

搜索帮助