Repository: shihuaping/gamex
Branch: master
Commit: 08e553880935
Files: 65
Total size: 66.8 KB
Directory structure:
gitextract_bpk30wdz/
├── .idea/
│ └── vcs.xml
├── README.md
├── center_svr/
│ ├── README.md
│ ├── center-handler.js
│ ├── center-server.js
│ ├── center.js
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── logger.js
│ ├── redis-oper.js
│ └── short-ID.js
├── game_svr/
│ ├── README.md
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── desk.js
│ ├── game-handler.js
│ ├── game-server.js
│ ├── game.js
│ ├── plugin/
│ │ ├── ddz-rule.js
│ │ └── ddz.js
│ ├── register-center.js
│ ├── room-list.js
│ └── room.js
├── gate_svr/
│ ├── README.md
│ ├── client-handler.js
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── connections.js
│ ├── gate.js
│ ├── logger.js
│ ├── redis-oper.js
│ ├── register-center.js
│ ├── server-handler.js
│ ├── short-ID.js
│ ├── sys-cache.js
│ ├── tcp-client.js
│ └── ws-server.js
├── hall_svr/
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── db-oper.js
│ ├── hall-handler.js
│ ├── hall-server.js
│ ├── hall.js
│ ├── logger.js
│ ├── register-center.js
│ └── short-ID.js
├── lib/
│ ├── black-ip.js
│ ├── const-define.js
│ └── rds-key.js
├── login_svr/
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── db-oper.js
│ ├── logger.js
│ ├── login-handler.js
│ ├── login.js
│ ├── register-center.js
│ ├── server.js
│ └── short-ID.js
├── package.json
├── protocol/
│ ├── client-protocol.md
│ ├── cmd-define.js
│ ├── packet.js
│ └── server-server-protocol.md
└── sql/
├── gamedb.sql
└── userdb.sql
================================================
FILE CONTENTS
================================================
================================================
FILE: .idea/vcs.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
================================================
FILE: README.md
================================================
# gamex
棋牌类游戏框架
技术基于node.js,写这个东西是因为最近接触了一部分棋牌的代码,过程实在是不那么让人愉快,代码规范性,可读性,稳定性,扩展性都不好。于是想用一个能够快速开发的工具来实现一个自己的框架。
center_svr 目前只做监控和服务器间的列表维护
gate_svr 用户请求转发到服务器
login_svr 用户帐号及登录授权
hall_svr 游戏信息及游戏服务器信息
game_svr 游戏服务器
除了center_svr以外,其它的服务器都可以水平扩展,可以多台分布式布署。center_svr是设计中的单点。
客户端与网关间应该只有一个连接,在开发阶段客户端可直连游戏服务器,不通过网关,降低调试复杂度。
对于多个同类服务器,使用随机访问的方式进行负载均衡。
服务器间通信协议都使用json,因为简单,好调试,易扩展。
服务间通信是自己模拟的rpc通信,需要严格的稳定性的话,要使用专门的rpc组件来实现。包括leader选举,广播等。目前的实现方式的问题在于,服务宕机了,不能及时通知到所有的服务。服务在调用时还需要自行判定被调者是否存活。
游戏服务器包含多个房间,每个房间参数可配置,每个房间里有一个桌子,每个桌子有玩家。由于node.js的天性,它是io处理强,cpu计算弱的。所以不要在游戏服务器中添加大量的计算类任务。
数据库使用SQL拼接的方式来写的。练手,练手。正确的做法是使用orm组件,避免大量重复的代码。
第三方游戏支付:
骏付通
爱贝
没想到这个项目有了这么多star……………………由于身体健康原因,项目停止了两年多了。
================================================
FILE: center_svr/README.md
================================================
中央服务器
功能:服务注册,服务查询
每一类服务器都有一个单独编号,编号是递增的。如果服务停掉了,这个编号可能会被下一个启动的服务使用。
每个服务都可以带有扩展信息,但是不建议使用,因为一旦添加了扩展信息就意味着中央服务器要修改,中央服务器修改后就需要重启。而中央服务器是单点,一重启就会导致客户端全部掉线。这个编号是给客户端用的,但同一个游戏有多个服务器的时候,用户连上网关以后,网关需要根据这个编号去找对应的服务器。否则就要保证服务端端口永不重复。
================================================
FILE: center_svr/center-handler.js
================================================
//-------------------------------------
// redis
// type : server1 server2 server3
//
//
//-------------------------------------
const rdsOper = require('./redis-oper');
const cmdDefine = require('../protocol/cmd-define');
const packet = require('../protocol/packet');
//serverInfo
// {
// name
// type
// ip
// port
// activeTime
// serverNo
// ext
// }
function registerServer(serverInfo) {
rdsOper.saveServer(serverInfo);
}
function removeServer() {
rdsOper.removeServer(serverInfo)
}
async function getServer(socket, serverInfo) {
let serverList = await rdsOper.getServerList(serverInfo.type);
}
function process(socket, data) {
let jObj = JSON.parse(data);
console.log(jObj);
let mainCmd = jObj.head.mcmd;
let subCmd = jObj.head.scmd;
switch (subCmd) {
//更新服务状态
case cmdDefine.SUB_CENTER_UPDATE:
let serverInfo = jObj.body;
serverInfo.ip = socket.remoteAddress;
serverInfo.activeTime = Math.floor(new Date().getTime()/1000);
registerServer(serverInfo);
break;
//查询服务可用列表
case cmdDefine.SUB_CENTER_GET:
let serverList = getServer(socket, serverInfo);
let retObj = packet.getPacket(mainCmd,subCmd+100);
retObj.body = serverList;
socket.write(retObj);
break;
default:
break
}
}
exports.process = process;
================================================
FILE: center_svr/center-server.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
const process = require('process');
const centerHandler = require('./center-handler');
const server = net.createServer();
//attention:
//1.to be more stable
//all request should put into a queue
function startServer() {
server.on('listening', function () {
logger.info("server is listening on port:%d", sysConfig.svrPort);
});
server.on('connection', function (socket) {
logger.info("connection income,ip:%s", socket.remoteAddress);
socket.setTimeout(60 * 1000, function () {
logger.error("ip:%s,idle timeout, disconncting, bye", socket.remoteAddress);
socket.end('idle timeout, disconnecting, bye!');
socket.destroy();
});
socket.on('data', function (data) {
logger.info("ip:%s, get data", socket.remoteAddress);
console.debug(data);
centerHandler.process(socket, data);
});
socket.on('error', function (err) {
logger.info("ip:%s,error", socket.remoteAddress);
logger.error(err);
socket.destroy();
});
socket.on('close', function (had_error) {
logger.info("ip:%s,socket closed", socket.remoteAddress);
if (!socket.destroyed) {
logger.info("destroy socket");
socket.destroy();
}
})
});
server.on('error', (err) => {
logger.error(err);
process.exit(1);
});
server.listen(sysConfig.svrPort, sysConfig.svrHost, () => {
logger.info('server bound on host:%s,port:%d', sysConfig.svrHost, sysConfig.svrPort);
});
}
exports.startServer = startServer;
================================================
FILE: center_svr/center.js
================================================
centerServer = require('./center-server');
centerServer.startServer();
================================================
FILE: center_svr/config/log4js.json
================================================
{
"appenders": {
"logfile": {
"type": "file",
"filename": "log/center.log",
"maxLogSize": 10485760,
"numBackups": 3
},
"console": {
"type": "console",
"level": "debug"
}
},
"categories": {
"default": {
"appenders": [
"console",
"logfile"
],
"level": "debug"
}
}
}
================================================
FILE: center_svr/config/sys-config.json
================================================
{
"svrHost":"127.0.0.1",
"svrPort":9200,
"redisHost":"127.0.0.1",
"redisPort":6379,
"redisPassword":""
}
================================================
FILE: center_svr/logger.js
================================================
const config = require('./config/log4js.json');
const log4js = require('log4js');
log4js.configure(config);
module.exports = log4js.getLogger();
================================================
FILE: center_svr/redis-oper.js
================================================
//
// 将服务信息保存在redis中,如果服务比较多,会造成冲突。每5秒一次心跳,极端情况下可能有服务永远注册不上。
// 仅有几个服务甚至十几个服务可以忽略上面的问题。有几十个服务就不要使用这种方式了,应该用rpc。
//
const sysConfig = require('./config/sys-config.json');
const redis = require('redis');
const rdsKey = require('../lib/rds-key');
const logger = require('./logger');
const host = sysConfig.redisHost;
const port = sysConfig.redisPort;
const password = sysConfig.redisPassword;
let rds = null;
function getConnection() {
if (rds) return rds;
if (!!password && password.length > 0) {
rds = redis.createClient({host: host, port: port, password: password});
} else {
rds = redis.createClient({host: host, port: port});
}
return rds;
}
getConnection();
//保存服务信息
function saveServer(serverInfo) {
let keyName = rdsKey.KEY_SERVER_TYPE + serverInfo.type;
rds.get(keyName, function (err, reply) {
if (err) {
logger.error(err);
return;
}
jArray = JSON.parse(reply);
if (!jArray) {
jArray = [];
}
let serverIDList = [];
for (let v of jArray) {
serverIDList.push(v.serverNo);
}
serverIDList.sort(function (a,b) {
if(a<b) return -1;
else if(a===b) return 0;
else return 1;
});
let serverId = 1;
let prevID = 0;
let count = 0;
for(let v in serverIDList) {
prevID = v;
count++;
if(count === 1) {
serverId = prevID;
continue;
}
// get a usefull id, TODO cocurrency problem
if(Math.abs(v-prevID) > 1) {
serverId = prevID + 1;
break;
}
}
// set server number
if(maxServerId > 1000) {
maxServerId = 1;
}
serverInfo.serverNo = maxServerId + 1;
let found = false;
for (let v of jArray) {
if (v.ip === serverInfo.ip) {
// do nothing
found = true;
break;
}
}
if (!found) {
jArray.push(serverInfo);
let json = JSON.stringify(jArray);
rds.set(keyName, json, 60*1000);
}
});
}
//查询可用服务列表
function getServerList(type) {
return new Promise(function (resolve, reject) {
let keyName = rdsKey.KEY_SERVER_TYPE + type;
rds.get(keyName, function (err, reply) {
if (err) {
reject(err);
return;
}
resolve(reply);
})
});
}
//移除掉线的服务
async function removeServer(serverInfo) {
let serverList = await getServerList(serverInfo.type);
let jArray = JSON.parse(serverList);
let idx = 0;
for (let v of jArray) {
if (v.ip === serverInfo.ip) {
delete jArray[idx];
break;
}
idx++;
}
let keyName = rdsKey.KEY_SERVER_TYPE + serverInfo.type;
let json = JSON.stringify(jArray);
rds.set(keyName, json, 60*1000);
}
exports.getServerList = getServerList;
exports.saveServer = saveServer;
exports.removeServer = removeServer;
================================================
FILE: center_svr/short-ID.js
================================================
var idx = 1024;
function getNextID() {
if(idx > 4200000000) {
idx = 1024;
}
idx++;
return idx;
}
exports.getNextID = getNextID;
================================================
FILE: game_svr/README.md
================================================
游戏服务器可以水平扩展。
游戏通过插件方式写在plugin目录下,同时将插件信息配置到config/sys-config中。
每个游戏一到多个游戏服务器,但不支持一个游戏服务器跑多个游戏。
每个游戏服务器有多个房间列表,房间相关配置信息在数据库中。玩家也可以自定义房间配置,覆盖默认的数据库中的配置。
游戏服务器启动后会自动往center服务器上报自己的信息。
游戏服务器会调用插件中的方法,如果插件不实现某个方法将会导致整个服务崩溃。
每个服务可开启的房间数是有上限的,现在限制为2000个房间。
================================================
FILE: game_svr/config/log4js.json
================================================
{
"appenders": {
"logfile": {
"type": "file",
"filename": "log/game.log",
"maxLogSize": 10485760,
"numBackups": 3
},
"console": {
"type": "console",
"level": "debug"
}
},
"categories": {
"default": {
"appenders": [
"console",
"logfile"
],
"level": "debug"
}
}
}
================================================
FILE: game_svr/config/sys-config.json
================================================
{
"svrHost":"127.0.0.1",
"svrPort":9000,
"redisHost":"127.0.0.1",
"redisPort":6379,
"redisPassword":"",
"centerSvrHost":"127.0.0.1",
"centerSvrPort":9200,
"plugin":"ddz.js"
}
================================================
FILE: game_svr/desk.js
================================================
const sysConfig = require('./config/sys-config.json');
const cmdDefine = require('../protocol/cmd-define');
function init() {
gameInst = require('./plugin/'+sysConfig.plugin);
}
function playerOpeCard(socket, packet) {
const cardEvent = packet.body.cardEvent;
switch (cardEvent.ope) {
case cmdDefine.EVENT_OUT_CARD:
gameInst.outCards(cardEvent);
break;
case cmdDefine.EVENT_PASS_CARD:
gameInst.passCards(cardEvent);
break;
default:
break;
}
}
================================================
FILE: game_svr/game-handler.js
================================================
const packet = require('../protocol/packet');
const cmdDefine = require('../protocol/cmd-define');
const room = require('./room');
function process(socket, data) {
let jObj = JSON.parse(data);
let mainCmd = jObj.head.mcmd;
let subCmd = jObj.head.scmd;
switch (subCmd) {
case cmdDefine.SUB_GAME_CREATE_ROOM: //创建房间
room.createRoom(socket, jObj);
break;
case cmdDefine.SUB_GAME_ENTER_ROOM: //加入房间
room.enterRoom(socket, jObj);
break;
case cmdDefine.SUB_GAME_LEAVE_ROOM: //离开房间
room.leaveRoom(socket, jObj);
break;
case cmdDefine.SUB_GAME_DISMISS_ROOM: //解散房间
room.dismissRoom(socket, jObj);
break;
case cmdDefine.SUB_GAME_REENTER_ROOM: //断线重进房间
room.reenterRoom(socket, jObj);
break;
case cmdDefine.SUB_GAME_QUERY_PLAYER_INFO: //查询玩家游戏状态
room.queryPlayerInfo(socket, jObj);
break;
case cmdDefine.SUB_GAME_DESK_SIT_DOWN: //坐下
room.playerSitDown(socket, jObj);
break;
case cmdDefine.SUB_GAME_DESK_READY: //准备
room.playerReady(socket, jObj);
break;
case cmdDefine.SUB_GAME_DESK_STAND_UP: //起立
room.playerStandUp(socket, jObj);
break;
case cmdDefine.SUB_GAME_DESK_WATCH: //观战
room.playerWatch(socket, jObj);
break;
case cmdDefine.SUB_GAME_DESK_OPE_CARD: //出牌,过牌,吃牌,碰,杠....等牌操作
room.playerOpeCard(socket, jObj);
break;
case cmdDefine.SUB_GAME_DESK_WIN: //胡牌
room.playerWin(socket, jObj);
break;
case cmdDefine.SUB_GAME_KICK_PLAYER: //房主踢人
room.kickPlayer(socket, jObj);
break;
default:
logger.error("unknown mainCmd:%d,subCmd:%d", mainCmd, subCmd);
break;
}
}
exports.process = process;
================================================
FILE: game_svr/game-server.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
const process = require('process');
const gameHandler = require('./gane-handler');
const server = net.createServer();
function startServer() {
server.on('listening', function () {
logger.info("server is listening on port:%d", sysConfig.svrPort);
});
server.on('connection', function (socket) {
socket.setTimeout(60 * 1000, function () {
logger.error("ip:%s,idle timeout, disconncting, bye", socket.remoteAddress);
socket.end('idle timeout, disconnecting, bye!');
socket.destroy();
});
socket.on('data', function (data) {
logger.info("ip:%s, get data", socket.remoteAddress);
console.debug(data);
gameHandler.process(socket, data);
});
socket.on('error', function (err) {
logger.info("ip:%s,error", socket.remoteAddress);
logger.error(err);
socket.destroy();
});
socket.on('close', function (had_error) {
logger.info("ip:%s,socket closed", socket.remoteAddress);
if (!socket.destroyed) {
logger.info("destroy socket");
socket.destroy();
}
})
});
server.on('error', (err) => {
logger.error(err);
process.exit(1);
});
server.listen(sysConfig.svrPort, sysConfig.svrHost, () => {
logger.info('server bound on host:%s,port:%d', sysConfig.svrHost, sysConfig.svrPort);
});
}
exports.startServer = startServer;
================================================
FILE: game_svr/game.js
================================================
const registerCenter = require('./register-center');
const gameServer = require('./game-server');
const sysConfig = require('./config/sys-config.json');
console.log(sysConfig);
setInterval(registerCenter.registerSelf, 5*1000);
gameServer.startServer();
================================================
FILE: game_svr/plugin/ddz-rule.js
================================================
/*
牌列表说明:
每个牌的每个花色都有一个单独的ID,一共54个ID,ID按黑红梅方的顺序生成
a,2,3,....q,k 黑桃 [1,2,3,4,5,6,7,8,9,10,11,12,13]
a,2,3,....q,k 红桃 [1,2,3,4,5,6,7,8,9,10,11,12,13] * 2
a,2,3,....q,k 梅花 [1,2,3,4,5,6,7,8,9,10,11,12,13] * 3
a,2,3,....1,k 方块 [1,2,3,4,5,6,7,8,9,10,11,12,13] * 4
小王
大王
*/
const assert = require('assert');
// 花色定义
let SUIT_TYPE = {
SPADE: 1, //黑桃
HEART: 2, //红桃
CLUB: 3, //梅花
DIAMOND: 4, //方块
JOKER: 5, //大小王
};
// 牌面显示定义
let CARD_CHAR = [
"null","A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "Kinglet", "King",
];
// 出牌类型
let CARD_TYPE = {
ILL: 0,
DAN: 1, //单张
DUI_ZI: 2, //对子
WANG_ZHA: 3, //王炸
ZHA_DAN: 4, //炸弹
SHUN_ZI: 5, //顺子
LIAN_DUI: 6, //连对
SAN: 7, //三张(不带)
SAN_DAI_YI: 8, //三带一
SAN_DAI_YI_DUI: 9, //三带一对
FEI_JI: 10, //飞机(不带)
FEI_JI_DAI_YI: 11, //飞机(带单)
FEI_JI_DAI_DUI: 12, //飞机(带对子)
SI_DAI_ER: 13, //四带二
SI_DAI_LIANG_DUI: 14, //四带两对
};
// 获取花色
// 3-q,k,a,2 每张牌有四个花色
function getSuitType(id) {
let suitType = null;
if (id >= 1 && id <= 13)
suitType = SUIT_TYPE.SPADE;
else if (id >= 14 && id <= 26)
suitType = SUIT_TYPE.HEART;
else if (id >= 27 && id <= 39)
suitType = SUIT_TYPE.CLUB;
else if (id >= 40 && id <= 52)
suitType = SUIT_TYPE.DIAMOND;
else if (id === 53 || id === 54)
suitType = SUIT_TYPE.JOKER;
return suitType
}
// 获取显示字
function getCardChar(id) {
let displayChar = null;
if (id >= 1 && id <= 52)
displayChar = CARD_CHAR[(id - 1) % 13 + 1];
else if (id === 53)
displayChar = CARD_CHAR[14];
else if (id === 54)
displayChar = CARD_CHAR[15];
return displayChar;
}
// 获取牌等级
// 大王 > 小王 > 2 > A > K .... > 3
function getGrade(id) {
let grade = 0;
if (id === 54) { //大王
grade = 17;
} else if (id === 53) { //小王
grade = 16;
} else {
let modResult = id % 13;
if (modResult === 2) // 2
grade = 15;
else if (modResult === 1) // A
grade = 14;
else if (modResult === 0) // K
grade = 13;
else if (modResult >= 3 && modResult < 13) // 3到Q
grade = modResult;
}
return grade;
}
let AllCards = [];
for (let i = 1; i <= 54; i++) {
AllCards[i] = {
id: i,
grade: getGrade(i), // 等级
suit: getSuitType(i), // 花色
display: getCardChar(i), // 牌面
};
}
console.log(AllCards);
function sortCards(cards) {
cards.sort(function (a, b) {
if(AllCards[a].grade > AllCards[b].grade) return -1;
else if(AllCards[a].grade === AllCards[b].grade) return 0;
else return 1;
});
}
function getGrades(cards) {
let t = {};
for (let v of cards) {
let grade = AllCards[v].grade;
if (! (grade in t)) {
t[grade] = 0;
}
t[grade] = t[grade] + 1;
}
return t;
}
function dump(cards) {
sortCards(cards);
for (let v of cards) {
console.log("花色:%d,牌面:%s, 权重:%d", AllCards[v].suit, AllCards[v].display, AllCards[v].grade);
}
}
//单张
function isDan(cards) {
return cards.length === 1;
}
assert(isDan([33]));
assert(isDan([23, 5]) === false);
//对子
function isDuiZi(cards) {
return cards.length === 2
&& AllCards[cards[0]].grade === AllCards[cards[1]].grade;
}
assert(isDuiZi([30, 43]));
assert(isDuiZi([30, 52]) === false);
//大小王炸弹
function isDuiWang(cards) {
return (cards.length === 2) && (cards[0] + cards[1] === 107);
}
assert(isDuiWang([53, 54]));
assert(isDuiWang([53, 52]) === false);
//三张
function isSan(cards) {
return cards.length === 3
&& (AllCards[cards[0]].grade === AllCards[cards[1]].grade)
&& (AllCards[cards[1]].grade === AllCards[cards[2]].grade);
}
assert(isSan([30, 43, 17])); //4 4 4
assert(isSan([30, 43, 15]) === false);//4 4 2
//三带一
function isSanDaiYi(cards) {
if (cards.length !== 4) {
return false;
}
sortCards(cards);
if (AllCards[cards[0]].grade !== AllCards[cards[1]].grade) {
let t = cards[3];
cards[3] = cards[0];
cards[0] = t;
}
if (AllCards[cards[0]].grade === AllCards[cards[1]].grade) {
if (AllCards[cards[0]].grade === AllCards[cards[2]].grade
&& AllCards[cards[0]].grade !== AllCards[cards[3]].grade) {
return true;
}
}
return false;
}
assert(isSanDaiYi([30, 43, 17, 5])); //4 4 4 3
assert(isSanDaiYi([30, 43, 17, 4]) === false); //4 4 4 4
//三带一对
function isSanDaiDui(cards) {
if (cards.length !== 5) {
return false;
}
sortCards(cards);
if (AllCards[cards[0]].grade !== AllCards[cards[2]].grade) {
let t = cards[3];
cards[3] = cards[0];
cards[0] = t;
t = cards[1];
cards[1] = cards[4] ;
cards[4] = t;
}
if (AllCards[cards[0]].grade === AllCards[cards[1]].grade) {
if (AllCards[cards[0]].grade === AllCards[cards[2]].grade
&& AllCards[cards[2]].grade !== AllCards[cards[3]].grade
&& AllCards[cards[3]].grade === AllCards[cards[4]].grade) {
return true;
}
} else {
return false;
}
return false;
}
assert(isSanDaiDui([30, 43, 17, 5, 31]));// 4 4 4 5 5
assert(isSanDaiDui([30, 43, 17, 3, 31]) === false);//4 4 4 5 3
//四张(炸弹)
function isSi(cards) {
if (cards.length !== 4) {
return false;
}
for (let i = 0; i < 3; i++) {
if (AllCards[cards[i]].grade !== AllCards[cards[i + 1]].grade) {
return false;
}
}
return true;
}
assert(isSi([30, 43, 17, 4]));// 4 4 4 4
assert(isSi([30, 43, 18, 4]) === false); // 4 4 5 4
//四带二
function isSiDaiEr(cards) {
if (cards.length !== 6) {
return false;
}
let grades = getGrades(cards);
for (let i in grades) {
if (grades[i] === 4) {
return true;
}
}
return false;
}
assert(isSiDaiEr([30, 43, 17, 4, 3, 5]));// 4 4 4 4 3 5
assert(isSiDaiEr([30, 43, 17, 4, 3, 16]));// 4 4 4 4 3 3
//四带两对
function isSiDaiLiangDui(cards) {
if (cards.length !== 8) {
return false
}
let grades = getGrades(cards);
let t1 = 0;
let t2 = 0;
let t3 = 0;
for (let i in grades) {
if (!t1) {
t1 = grades[i]
} else if (!t2) {
t2 = grades[i];
} else if (!t3) {
t3 = grades[i];
}
}
let arr = [t1, t2, t3];
arr.sort(function (a, b) {
if( a > b) { return -1;}
else if(a===b) {return 0;}
else {return 1;}
});
return (arr.length === 3) &&
(arr[0] === 4) && (arr[1] === 2) && (arr[2] === 2);
}
assert(isSiDaiLiangDui([30, 43, 17, 4, 3, 16, 5, 18]));// 4 4 4 4 3 3 5 5
assert(isSiDaiLiangDui([3, 16, 5, 18, 30, 43, 17, 4]));// 3 3 5 5 4 4 4 4
assert(isSiDaiLiangDui([30, 43, 17, 4, 3, 6, 5, 18]) === false); // 4 4 4 4 3 6 5 5
//顺子
function isShunZi(cards) {
// 顺子必须超过五张
if (cards.length < 5)
return false;
sortCards(cards);
//最大的牌不能超过A
if (AllCards[cards[0]].grade > 14)
return false;
//如果不连续
for (let i=0;i<cards.length-1;i++) {
if (AllCards[cards[i]].grade !== AllCards[cards[i + 1]].grade + 1)
return false;
}
return true;
}
assert(isShunZi([13, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1]));
assert(isShunZi([35, 21, 20, 6, 31, 4]));//9 8 7 6 5 4
assert(isShunZi([21, 20, 4, 31]) === false); //9 8 7 4 5
assert(isShunZi([35, 21, 5, 6, 31]) === false); //9 8 5 6 5 4
//连对
function isLianDui(cards)
{
//连对必须有三对以上
if (cards.length < 6)
return false;
//连对必须牌数是偶数
if (cards.length % 2 === 1)
return false;
sortCards(cards);
//最大的牌不能超过A
if (AllCards[cards[0]].grade > 14)
return false;
//如果牌不连续
for (let i=0; i < cards.length / 2 - 1; i++) {
//先判定是不是都是对子
if (AllCards[cards[i * 2]].grade !== AllCards[cards[i * 2 + 1]].grade)
return false;
//再判定连续对
if (i < cards.length / 2 - 1
&& AllCards[cards[i * 2 + 1]].grade !== AllCards[cards[(i+1) * 2 ]].grade + 1)
return false;
}
return true;
}
assert(isLianDui([35, 9, 21, 8, 20, 7]));// 9 9 8 8 7 7
assert(isLianDui([35, 9, 21, 8, 20, 7, 6, 19]));// 9 9 8 8 7 7 6 6
assert(isLianDui([35, 9, 21, 8, 20, 7, 33]) === false);// 9 9 8 8 7 7 7
//飞机(不带)
function isFeiJi(cards) {
if (cards.length !== 6 && cards.length !== 9)
return false;
let grades = getGrades(cards);
// 如果不是三张,就判错
for (let i in grades) {
if (grades[i] !== 3)
return false;
}
//如果是三张组合,还要判定是不是连续的
let t1 = 0;
let t2 = 0;
let t3 = 0;
for(k in grades) {
if (grades[k] === 3) {
if (!t1) t1 = Number(k);
else if(!t2) t2 = Number(k);
else if(!t3) t3 = Number(k);
}
}
if (cards.length === 6 && t1 && t2 && (t1 === t2 + 1 || t2 === t1 + 1))
return true;
if (cards.length === 9 && t1 && t2 && t3) {
let arr = [t1, t2, t3];
arr.sort(function (a, b) {
if (a > b) return -1;
else if (a === b) return 0;
else return 1;
});
if (arr[0] === arr[1] + 1 && arr[1] === arr[2] + 1)
return true;
}
return false;
}
assert(isFeiJi([35, 9, 22, 21, 8, 34]));// 9 9 9 8 8 8
assert(isFeiJi([35, 9, 22, 21, 8, 34, 7, 20, 33]));// 9 9 9 8 8 8 7 7 7
assert(isFeiJi([35, 9, 22, 20, 7, 33]) === false);// 9 9 9 7 7 7
//飞机(带单张)
function isFeiJiDaiDan(cards) {
if (cards.length !== 8 && cards.length !== 12)
return false;
let grades = getGrades(cards);
let t1 = 0;
let t2 = 0;
let t3 = 0;
for(let k in grades) {
if (grades[k] === 3) {
if (!t1) t1 = Number(k);
else if(!t2) t2 = Number(k);
else if(!t3) t3 = Number(k);
}
}
//判定三张是否连续
if (cards.length === 8 && t1 && t2 && (t1 === t2 + 1 || t2 === t1 + 1))
return true;
if (cards.length === 12 && t1 && t2 && t3) {
let arr = [t1, t2, t3];
arr.sort(function (a,b) {
if (a>b)return -1;
else if(a===b) return 0;
else return 1;
});
if (arr[0] === arr[1] + 1 && arr[1] === arr[2] + 1)
return true;
}
return false;
}
assert(isFeiJiDaiDan([35, 9, 22, 21, 8, 34, 3, 4]));// 9 9 9 8 8 8 3 4
assert(isFeiJiDaiDan([35, 9, 22, 21, 8, 34, 7, 20, 33, 3, 4, 5]));// 9 9 9 8 8 8 3 4 5
assert(isFeiJiDaiDan([35, 9, 22, 20, 7, 33, 3, 4]) === false);// 9 9 9 7 7 7
//飞机(带对子)
function isFeiJiDaiDui(cards) {
if (cards.length !== 10 && cards.length !== 15)
return false;
let grades = getGrades(cards);
let t1 = 0;
let t2 = 0;
let t3 = 0;
for(let k in grades) {
if (grades[k] === 3) {
if (!t1) t1 = Number(k);
else if(!t2) t2 = Number(k);
else if(!t3) t3 = Number(k);
} else if(grades[k] !== 2) {
return false;
}
}
if (cards.length === 10 && t1 && t2 && (t1 === t2 + 1 || t2 === t1 + 1) )
return true;
if (cards.length === 15 && t1 && t2 && t3) {
let arr = [t1, t2, t3];
arr.sort(function (a, b) {
if (a > b) return -1;
else if (a === b) return 0;
else return 1;
});
if (arr[0] === arr[1] + 1 && arr[1] === arr[2] + 1)
return true;
}
return false;
}
assert(isFeiJiDaiDui([35, 9, 22, 21, 8, 34, 7, 20, 33, 3, 16, 4, 17, 5, 18]));//9 9 9 8 8 8 7 7 7 3 3 4 4 5 5
assert(isFeiJiDaiDui([35, 9, 22, 21, 8, 34, 3, 4, 16, 17]));// 9 9 9 8 8 8 3 3 4 4
assert(isFeiJiDaiDui([35, 9, 22, 21, 8, 34, 3, 4, 16, 18]) === false); // 9 9 9 8 8 8 3 3 4 5
function getCardType(cards) {
let c = cards.length;
if (c === 1) { //单张
return CARD_TYPE.DAN;
} else if (c === 2) { //两张只可能是王炸或一对
if (isDuiWang(cards)) {
return CARD_TYPE.WANG_ZHA;
} else if (isDuiZi(cards)) {
return CARD_TYPE.DUI_ZI;
}
} else if (c === 3) { //三张只可能是单出三张
if (isSan(cards))
return CARD_TYPE.SAN;
} else if (c === 4) { //四张只可能是炸弹或三带一
if (isSi(cards))
return CARD_TYPE.ZHA_DAN;
else if (isS && aiYi(cards))
return CARD_TYPE.SAN_DAI_YI;
} else if (c === 5) { //5张只可能是顺子或三带一对
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
else if (isSanDaiDui(cards))
return CARD_TYPE.SAN_DAI_YI_DUI;
} else if (c === 6) { //6张可能是顺子、飞机(不带)、四带二、连对
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
else if (isFeiJi(cards))
return CARD_TYPE.FEI_JI;
else if (isSiDaiEr(cards))
return CARD_TYPE.SI_DAI_ER;
else if (isLianDui(cards))
return CARD_TYPE.LIAN_DUI;
} else if (c === 7 || c === 11) { //7或11张只可能是顺子
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
} else if (c === 8) { //8张可能是顺子、连对、飞机(带单)
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
else if (isFeiJiDaiDan(cards))
return CARD_TYPE.FEI_JI;
else if (isLianDui(cards))
return CARD_TYPE.LIAN_DUI;
} else if (c === 9) { //9张只可能是顺子、飞机(3头不带)
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
else if (isFeiJi(cards))
return CARD_TYPE.FEI_JI;
} else if (c === 10) { //10张可能是顺子、飞机(2头带对子)、连对
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
else if (isFeiJiDaiDui(cards))
return CARD_TYPE.FEI_JI;
else if (isLi && ui(cards))
return CARD_TYPE.LIAN_DUI;
} else if (c === 12) { //12张可能是顺子、飞机(3头带单张)、连对
if (isShunZi(cards))
return CARD_TYPE.SHUN_ZI;
else if (isFeiJiDaiDan(cards))
return CARD_TYPE.FEI_JI;
else if (isLi && ui(cards))
return CARD_TYPE.LIAN_DUI;
} else if (c ===13) {
//13张不合法,顺子带'2',不可能是飞机,不可能是4带
} else if (c === 14) { //14张只可能是连对
if (isLi && ui(cards))
return CARD_TYPE.LIAN_DUI;
} else if (c === 15) { //15张只可能是飞机(3头带对子)
if (isFeiJiDaiDui(cards))
return CARD_TYPE.FEI_JI;
}
return CARD_TYPE.ILL;
}
assert(getCardType([54]) === CARD_TYPE.DAN);
assert(getCardType([54, 53]) === CARD_TYPE.WANG_ZHA);
assert(getCardType([3, 3]) === CARD_TYPE.DUI_ZI);
assert(getCardType([3, 3, 4, 4, 5, 5]) === CARD_TYPE.LIAN_DUI);
assert(getCardType([3, 4, 5, 6, 7]) === CARD_TYPE.SHUN_ZI);
assert(getCardType([13, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1]) === CARD_TYPE.SHUN_ZI);
assert(getCardType([3, 3, 3, 3]) === CARD_TYPE.ZHA_DAN);
assert(getCardType([3, 3, 3, 3, 4, 4, 4, 4]) !== CARD_TYPE.FEI_JI);
assert(getCardType([3, 4, 5, 6, 7, 8, 9]) === CARD_TYPE.SHUN_ZI);
assert(getCardType([3, 3, 3, 4, 4, 4, 5, 5, 5]) === CARD_TYPE.FEI_JI);
assert(getCardType([3, 3, 3, 1, 4, 4, 4, 2, 5, 5, 5, 7]) === CARD_TYPE.FEI_JI);
assert(getCardType([3, 3, 3, 1, 1, 4, 4, 4, 2, 2, 5, 5, 5, 7, 7]) === CARD_TYPE.FEI_JI);
assert(getCardType([3, 3, 4, 4, 2, 2]) === CARD_TYPE.ILL);
assert(getCardType([3, 3, 3, 4, 2]) === CARD_TYPE.ILL);
assert(getCardType([3, 3, 3, 4, 4, 4, 2]) === CARD_TYPE.ILL);
assert(getCardType([2, 3, 4, 5, 6]) === CARD_TYPE.ILL);
assert(getCardType([3, 4, 5, 6, 7, 7]) === CARD_TYPE.ILL);
////////////////////////////////////////////-
// 获取牌信息,花色,牌面,权重
function getCardDetail(card) {
return AllCards[card];
}
// 将多张牌排序后返回牌信息
function getSortedCardsDetail(cards) {
sortCards(cards);
let cardDetails = [];
for (let i in cards) {
cardDetails[i] = getCardDetail(cards[i]);
}
return cardDetails;
}
// 检查能不能出牌
function checkCards(preCards, curCards) {
let card_type = getCardType(curCards);
if (card_type === CARD_TYPE.ILL)
return false;
//头次打
if (preCards === null)
return true;
// 上家牌类型
let pre_act = getCardType(preCards);
if (card_type === CARD_TYPE.WANG_ZHA) { // 王炸无视任何牌
return true;
} else if (card_type === CARD_TYPE.ZHA_DAN) { //出炸弹,炸弹需要和上家比大小
if (pre_act === CARD_TYPE.WANG_ZHA) {
return false;
} else if (pre_act === CARD_TYPE.ZHA_DAN) {
if (AllCards[preCards[1]].grade < AllCards[curCards[1]].grade)
return true;
else
return false;
} else {
return true;
}
} else if (pre_act !== card_type) { // 和上家比牌型,上家出什么自己也要出什么
return false;
} else if (preCards.length !== curCards.length) {
return false;
} else { // 牌型一样,但必须比上家大
sortCards(preCards);
sortCards(curCards);
if (AllCards[preCards[1]].grade < AllCards[curCards[1]].grade)
return true;
else
return false;
}
return false;
}
function getAllCards() {
return AllCards;
}
exports.checkCards = checkCards;
exports.getCardDetail = getCardDetail;
exports.getSortedCardsDetail = getSortedCardsDetail;
exports.getCardType = getCardType;
exports.getAllCards = getAllCards;
exports.dump = dump;
================================================
FILE: game_svr/plugin/ddz.js
================================================
const ddzRule = require('./ddz-rule');
class DouDiZhu {
constructor() {
//牌池
this.cardPool = [];
//座位
this.seats = {};
//地主
this.diZhu = 0;
//底牌
this.diPai = [];
}
//初始化
initGame() {
let full = [];
let allCards = ddzRule.getAllCards();
for(let i=0;i<54;i++) {
full.push(allCards[i+1].id);
}
let pos = 1;
this.seats[pos] = {};
this.seats[pos].handCards = []; //手上的牌
this.seats[pos].outCards = []; //已经打出的牌
//洗牌,每人发17张
let arrLength = full.length;
for(let i=0;i<17;i++) {
let j = Math.floor(Math.random()*arrLength);
this.seats[pos].handCards.push(full[j]);
full.splice(j,1);
arrLength--;
}
pos = 2;
this.seats[pos] = {};
this.seats[pos].handCards = []; //手上的牌
this.seats[pos].outCards = []; //已经打出的牌
for(let i=0;i<17;i++) {
let j = Math.floor(Math.random()*arrLength);
this.seats[pos].handCards.push(full[j]);
full.splice(j,1);
arrLength--;
}
pos = 3;
this.seats[pos] = {};
this.seats[pos].handCards = []; //手上的牌
this.seats[pos].outCards = []; //已经打出的牌
for(let i=0;i<17;i++) {
let j = Math.floor(Math.random()*arrLength);
this.seats[pos].handCards.push(full[j]);
full.splice(j,1);
arrLength--;
}
//留下三张给地主
this.diPai = full;
//ddzRule.dump(this.seats[1].handCards);
//ddzRule.dump(this.seats[2].handCards);
//ddzRule.dump(this.seats[3].handCards);
//ddzRule.dump(full);
}
//发牌
sendCards() {
}
//出牌
outCards() {
}
//过牌
passCards() {
}
//叫地主
qiangDiZhu() {
}
}
let ddz = new DouDiZhu();
ddz.initGame();
================================================
FILE: game_svr/register-center.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const packet = require('../protocol/packet');
const cmdDefine = require('../protocol/cmd-define');
const constDefine = require('../lib/const-define');
const shortID = require('./short-ID');
const logger = require('./logger');
let conn = null;
function registerSelf() {
let port = sysConfig.centerSvrPort;
let host = sysConfig.centerSvrHost;
console.log("connec to host:%s,port:%d", host, port);
conn = net.createConnection({port:port,host:host}, function () {
const psudoID = shortID.getNextID();
conn.psudoID = psudoID;
conn.ip = host;
let jObj = packet.getPacket(cmdDefine.CENTER, cmdDefine.SUB_CENTER_UPDATE);
let serverInfo = {};
serverInfo.type = constDefine.SERVER_TYPE_GAME;
serverInfo.port = sysConfig.svrPort;
serverInfo.name = "game_svr";
jObj.body = serverInfo;
let json = JSON.stringify(jObj);
console.log(json);
conn.write(json);
conn.destroy();
});
conn.on('end', function () {
logger.info("server connection closed,fd:%d,ip:%s", conn.psudoID, conn.ip);
});
conn.on('error', function (err) {
logger.info("server connection error,fd:%d,ip:%s", conn.psudoID, conn.ip);
logger.error(err);
});
conn.on('data', function (data) {
console.log("server fd:%d get data:%s", conn.psudoID, data);
});
}
exports.registerSelf = registerSelf;
================================================
FILE: game_svr/room-list.js
================================================
//
// 房间号生成规则:游戏ID+6个数字,6个数字随机生成,每次服务重启生成的数字都不一样
// 避免被猜到房间号,每个房间号带一个4位随机码,获取房间的时候动态生成
//
//
// [1,2,3,4,5] 方便随机数获取
// {1:"room1",2:"room2"....}
//
roomIDList = [];
roomList = {};
const Room = require('./room');
//初始化房间列表
function initRoomList(gid,maxRoomCount) {
for (let i=0;i<maxRoomCount;i++) {
roomIDList.push(Math.ceil(Math.random()*100000));
}
//随机数有可能有重复,无所谓了
for(v in roomIDList) {
let r = new Room();
r.roomID = v;
r.gid = gid;
roomList[v] = r;
}
}
//获取一个可用的房间
function getRoom() {
let max = roomIDList.length;
let pos = Math.ceil(max * Math.random());
//从0开始
let k = pos-1;
//fuck this api
roomList.splice(k, 1);
let r = roomList[k];
return r;
}
//销毁房间
function destroyRoom(roomId) {
roomIDList.push(roomId);
let r = new Room();
r.roomID = roomId;
r.gid = gid;
roomList[roomId] = r;
}
exports.initRoomList = initRoomList;
exports.getRoom = getRoom;
================================================
FILE: game_svr/room.js
================================================
// room - desk
//房间列表暂时不存数据库了
const desk = require('./desk');
//创建一个房间,实际是从已有房间里取一个
function createRoom(socket, packet) {
}
//进入房间。可以游戏,可以围观
function enterRoom(socket, packet) {
}
//离开房间
function leaveRoom(socket, packet) {
}
//解散房间,将房间归还到房间列表
function dismissRoom(socket, packet) {
}
//这个掉线重入,是不是要这么设计还在取舍中
function reenterRoom(socket, packet) {
}
//进房间后,要查询玩家列表
function queryPlayInfo(socket, packet) {
}
//坐桌子
function playerSitDown(socket, packet) {
}
//准备游戏,准备人数满员就开始游戏
function playerReady(socket, packet) {
}
//离开桌子
function playerStandUp(socket, packet) {
}
//围观,主要是广播
function playerWatch(socket, packet) {
}
//游戏中
function playerOpeCard(socket, packet) {
desk.playerOpeCard(socket, packet);
}
//赢了
function playerWin(socket, packet) {
}
//房主踢人,服务器踢+客户端通知
function kickPlayer(socket, packet) {
}
exports.createRoom = createRoom;
exports.enterRoom = enterRoom;
exports.dismissRoom = dismissRoom;
exports.reenterRoom = reenterRoom;
exports.leaveRoom = leaveRoom;
exports.queryPlayerInfo = queryPlayInfo;
exports.playerSitDown = playerSitDown;
exports.playerReady = playerReady;
exports.playerStandUp = playerStandUp;
exports.playerWatch = playerWatch;
exports.playerOpeCard = playerOpeCard;
exports.playerWin = playerWin;
exports.kickPlayer = kickPlayer;
================================================
FILE: gate_svr/README.md
================================================
gate服务的设计原则
gate服务按固定时间间隔上报状态到center服务器
gate服务按固定时间间隔获取所有类型服务器列表(列表不能过超过1M大小,超过了redis会有卡顿现象)
gate服务器在玩家登陆请求时转发请求到登陆服务器
登录成功后,gate服务器在收到客户端连接游戏服务器请求时,在指定的游戏服务器建立连接,打通客户端和游戏服务器
gate服务器在玩家游戏时,将请求转发到固定的游戏服务器,带上一个玩家序号,这个序号可以是固定的玩家ID,没有玩家ID可以是一个伪ID,类似于socket fd这样的东西。(伪ID在被攻击时会产生严重的问题,就是ID耗尽溢出,回绕的时候怎么和正常ID不冲突,这个问题现在是没处理的)
游戏服务器回包以后,gate服务器通过包内的玩家序号找到对应的玩家(注意不是请求序号,如果是请求序号一个玩家可能对应多个请求)
为了解耦模块间的关联,gate服务器对消息的处理应该统一化,尽量不要有特殊处理。或者说是做成插件化,特殊的业务都在插件中完成
================================================
FILE: gate_svr/client-handler.js
================================================
//
//
// 客户端请求进来时的处理
//
//
const logger = require('./logger');
const connections = require('./connections');
const cmdDefine = require('../protocol/cmd-define');
function onConnection(fd,ws) {
logger.info("client connected,fd:%d,ip:%s", fd, ws.ip);
connections.addClientConn(fd,ws);
}
function onMessage(ws,msg) {
logger.info("client read data,fd:%d,ip:%s", ws.psudoID, ws.ip);
connections.updateClient(ws);
var jObj = JSON.parse(msg);
var mainCmd = jObj.head.mcmd;
var subCmd = jObj.head.scmd;
switch (mainCmd) {
case cmdDefine.HEART_BEAT:
ws.send(msg);
break;
case cmdDefine.LOGIN:
default:
logger.error("client fd:%d send unkown cmd:%d", ws.psudoID, mainCmd);
}
}
function onError(ws,err) {
logger.error("client error,fd:%d,ip:%s", fd, ws.ip);
logger.error(err);
const fd = ws.psudoID;
connections.removeClientConn(fd);
}
function onClose(ws) {
logger.info("client close,fd:%d,ip:%s", fd, ws.ip);
const fd = ws.psudoID;
connections.removeClientConn(fd);
}
================================================
FILE: gate_svr/config/log4js.json
================================================
{
"appenders": {
"logfile": {
"type": "file",
"filename": "log/gate.log",
"maxLogSize": 10485760,
"numBackups": 3
},
"console": {
"type": "console",
"level": "debug"
}
},
"categories": {
"default": {
"appenders": [
"console",
"logfile"
],
"level": "debug"
}
}
}
================================================
FILE: gate_svr/config/sys-config.json
================================================
{
"svrHost":"127.0.0.1",
"svrPort":9000,
"redisHost":"127.0.0.1",
"redisPort":6379,
"redisPassword":"",
"centerSvrHost":"127.0.0.1",
"centerSvrPort":9200
}
================================================
FILE: gate_svr/connections.js
================================================
var clientConns = {};
var svrConns = {};
var mapConns = {};
function addClientConn(fd,conn) {
clientConns[fd] = {conn:conn,updateTime:new Date().getTime()};
}
function updateClientConn(conn) {
conn.updateTime = new Date().getTime();
}
function addSvrConn(fd, conn) {
svrConns[fd] = {conn:conn,updateTime:new Date().getTime()};
}
function addMapConn(fdClient, fdSvr) {
mapConns[fdClient] = fdSvr;
mapConns[fdSvr] = fdClient;
}
function updateSvrConn(conn) {
conn.updateTime = new Date().getTime();
}
function getClientConn(fd) {
if (fd in clientConns) {
return clientConns[fd].conn;
}
return null;
}
function getSvrConn(fd) {
if (fd in svrConns) {
return svrConns[fd].conn;
}
return null;
}
function getMapConn(fd) {
if (fd in mapConns) {
return mapConns[fd];
}
return 0;
}
function removeSvrConn(fd) {
const clientFd = getMapConn(fd);
if (!!clientFd) {
delete mapConns[clientFd];
delete mapConns[fd];
}
if (fd in svrConns) {
delete svrConns[fd];
}
if (clientFd in clientConns) {
const conn = clientConns[clientFd];
if (!!conn) conn.close();
delete clientConns[clientFd];
}
}
function removeClientConn(fd) {
const svrFd = getMapConn(fd);
if (!!svrFd) {
delete mapConns[svrFd];
delete mapConns[fd];
}
if (fd in clientConns) {
delete clientConns[fd];
}
if (svrFd in svrConns) {
const conn = [svrFd];
if (!!conn) conn.destroy();
delete svrConns[svrFd];
}
}
function checkTimeout() {
var outTimeFd = [];
const now = new Date().getTime();
for (k in clientConns) {
if ((now - clientConns[k].updateTime) > 60*1000) {
outTimeFd.push(k);
}
}
for (var i=0; i<outTimeFd.length;i++ ) {
logger.error("client fd:%d timeout", outTimeFd[i]);
const fd = outTimeFd[i];
if (fd in clientConns) {
delete clientConns[fd];
const svrFd = mapConns[fd];
if (!!svrFd) {
if (svrFd in svrConns) {
svrConns[svrFd].conn.destroy();
delete svrConns[svrFd];
}
}
}
}
outTimeFd = []
for (k in svrConns) {
if((now - svrConns[k].updateTime) > 60*1000) {
outTimeFd.push(k);
}
}
for (var i=0; i<outTimeFd.length;i++ ) {
logger.error("server fd:%d timeout", outTimeFd[i]);
const fd = outTimeFd[i];
if (fd in clientConns) {
svrConns[fd].conn.destroy();
delete svrConns[fd];
const clientFd = mapConns[fd];
if (!!clientFd) {
if (clientFd in clientConns) {
clientConns[svrFd].conn.close();
delete clientConns[svrFd];
}
}
}
}
}
setInterval(checkTimeout, 10*1000);
exports.addClientConn = addClientConn;
exports.updateClientConn = updateClientConn;
exports.addSvrConn = addSvrConn;
exports.updateSvrConn = updateSvrConn;
exports.getClientConn = getClientConn;
exports.getSvrConn = getSvrConn;
exports.addMapConn = addMapConn;
exports.getMapConn = getMapConn;
exports.removeClientConn = removeClientConn;
exports.removeSvrConn = removeSvrConn;
================================================
FILE: gate_svr/gate.js
================================================
const wsServer = require('./ws-server');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
const registerCenter = require('./register-center');
const sysCache = require('./sys-cache');
logger.debug(sysConfig);
sysCache.readCached();
setInterval(registerCenter.registerSelf, 5*1000);
wsServer.startServer(sysConfig);
================================================
FILE: gate_svr/logger.js
================================================
const config = require('./config/log4js.json');
const log4js = require('log4js');
log4js.configure(config);
module.exports = log4js.getLogger();
================================================
FILE: gate_svr/redis-oper.js
================================================
const sysConfig = require('./config/sys-config.json');
const redis = require('redis');
const redisKey = require('../lib/rds-key');
var host = sysConfig.redisHost;
var port = sysConfig.redisPort;
var password = sysConfig.redisPassword;
function readCachedServers(cb) {
var rds = null;
if (!!password && password.length > 0) {
rds = redis.createClient({host:host,port:port,password:password});
} else {
rds = redis.createClient({host:host,port:port});
}
for (let v of redisKey.SERVER_TYPE_LIST) {
let key = redisKey.KEY_SERVER_TYPE + v;
rds.get(key, function (error,reply) {
cb (v, reply);
});
}
}
exports.readCachedServers = readCachedServers;
================================================
FILE: gate_svr/register-center.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const packet = require('../protocol/packet');
const cmdDefine = require('../protocol/cmd-define');
const constDefine = require('../lib/const-define');
const shortID = require('./short-ID');
const logger = require('./logger');
let conn = null;
function registerSelf() {
let port = sysConfig.centerSvrPort;
let host = sysConfig.centerSvrHost;
console.log("connec to host:%s,port:%d", host, port);
conn = net.createConnection({port:port,host:host}, function () {
const psudoID = shortID.getNextID();
conn.psudoID = psudoID;
conn.ip = host;
let jObj = packet.getPacket(cmdDefine.CENTER, cmdDefine.SUB_CENTER_UPDATE);
let serverInfo = {};
serverInfo.type = constDefine.SERVER_TYPE_GATE;
serverInfo.port = sysConfig.svrPort;
serverInfo.name = "gate_svr";
jObj.body = serverInfo;
let json = JSON.stringify(jObj);
console.log(json);
conn.write(json);
conn.destroy();
});
conn.on('end', function () {
logger.info("server connection closed,fd:%d,ip:%s", conn.psudoID, conn.ip);
});
conn.on('error', function (err) {
logger.info("server connection error,fd:%d,ip:%s", conn.psudoID, conn.ip);
logger.error(err);
});
conn.on('data', function (data) {
console.log("server fd:%d get data:%s", conn.psudoID, data);
});
}
exports.registerSelf = registerSelf;
================================================
FILE: gate_svr/server-handler.js
================================================
//
//
// 服务器返回数据时的处理
//
//
const logger = require('./logger');
const connections = require('./connections');
function onConnection(fd,tcpConn) {
logger.info("client connected,fd:%d,ip:%s", fd, tcpConn.ip);
connections.addSvrConn(fd,tcpConn);
}
function onMessage(tcpConn,msg) {
logger.info("client read data,fd:%d,ip:%s", fd, tcpConn.ip);
connections.updateSvrConn(tcpConn);
var jObj = JSON.parse(msg);
}
function onError(tcpConn,err) {
logger.error("client error,fd:%d,ip:%s", fd, tcpConn.ip);
logger.error(err);
const fd = ws.psudoID;
connections.removeSvrConn(fd);
}
function onClose(tcpConn) {
logger.info("client close,fd:%d,ip:%s", fd, tcpConn.ip);
const fd = tcpConn.psudoID;
connections.removeSvrConn(fd);
}
================================================
FILE: gate_svr/short-ID.js
================================================
var idx = 1024;
function getNextID() {
if(idx > 4200000000) {
idx = 1024;
}
idx++;
return idx;
}
exports.getNextID = getNextID;
================================================
FILE: gate_svr/sys-cache.js
================================================
const redisOper = require('./redis-oper');
var cached = {};
function saveServerInfo(type,serverList) {
let jArray = JSON.parse(serverList);
cached[type] = jArray;
}
// read servers info from redis
function readCached() {
redisOper.readCachedServers(saveServerInfo);
}
function getServerList(type) {
return cached[type];
}
setInterval(readCached, 60*1000);
exports.getServerList = getServerList;
exports.readCached = readCached;
================================================
FILE: gate_svr/tcp-client.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const serverHandler = require('./server-handler');
const logger = require('./logger');
const shortID = require('./short-ID');
function getNewConnection(port,host) {
if (!port) port = sysConfig.centerSvrPort;
if (!host) host = sysConfig.centerSvrHost;
var conn = net.createConnection({port:port,host:host}, function () {
const psudoID = shortID.getNextID();
conn.psudoID = psudoID;
conn.ip = host;
logger.info("connect to server,ip:%s,port:%d", host, port);
serverHandler.onConnect(psudoID, conn);
});
conn.on('end', function () {
logger.info("server connection closed,fd:%d,ip:%s", conn.psudoID, conn.ip);
serverHandler.onClose(conn);
});
conn.on('error', function (err) {
logger.info("server connection error,fd:%d,ip:%s", conn.psudoID, conn.ip);
logger.error(conn,err);
serverHandler.onError(conn);
});
conn.on('data', function (data) {
console.log("server fd:%d get data:%s", conn.psudoID, data);
serverHandler.onData(data);
});
};
exports.getNewConnection = getNewConnection;
================================================
FILE: gate_svr/ws-server.js
================================================
const WebSocket = require('ws');
const clientHandler = require('./client-handler');
const shortID = require('./short-ID');
function startServer(sysConfig) {
const host = sysConfig.svrHost;
const port = sysConfig.svrPort;
const wss = new WebSocket.Server({host:host,port:port});
wss.on('connection', function (ws) {
const psudoID = shortID.getNextID();
const ip = req.connection.remoteAddress;
ws.psudoID = psudoID;
ws.ip = ip;
clientHandler.onConnection(ws);
ws.on('message', function (msg) {
clientHandler.onMessage(ws, msg);
});
ws.on('error', function (err) {
clientHandler.onError(ws,err);
});
ws.on('close', function () {
clientHandler.onClose(ws);
});
});
}
exports.startServer = startServer;
================================================
FILE: hall_svr/config/log4js.json
================================================
{
"appenders": {
"logfile": {
"type": "file",
"filename": "log/hall.log",
"maxLogSize": 10485760,
"numBackups": 3
},
"console": {
"type": "console",
"level": "debug"
}
},
"categories": {
"default": {
"appenders": [
"console",
"logfile"
],
"level": "debug"
}
}
}
================================================
FILE: hall_svr/config/sys-config.json
================================================
{
"svrHost":"127.0.0.1",
"svrPort":9300,
"centerSvrHost":"127.0.0.1",
"centerSvrPort":9200,
"redisHost":"127.0.0.1",
"redisPort":6379,
"redisPassword":"",
"mysqlHost":"127.0.0.1",
"mysqlPort":3306,
"mysqlUsername":"root",
"mysqlPassword":"123456"
}
================================================
FILE: hall_svr/db-oper.js
================================================
const mysql = require('mysql');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
var pool = null;
function getPool() {
pool = mysql.createPool(
{
connectionLimit: 50,
host: sysConfig.mysqlHost,
port: sysConfig.mysqlPort,
user: sysConfig.mysqlUsername,
password: sysConfig.mysqlPassword,
database: 'gamedb'
}
);
}
getPool();
function getGameList() {
return new Promise(function (resolve, reject) {
try {
//avoid sql injection
let sql = 'select * from game_list';
console.log(sql);
pool.getConnection(function (err, conn) {
conn.query(sql, function (err, results, fields) {
conn.release();
if (err) {
throw err;
}
resolve(results);
});
});
} catch (ex) {
logger.error(ex);
reject(ex);
}
});
}
function getGameServerList(gameID) {
return new Promise(function (resolve, reject) {
try {
let sql = "select * from game_server_list where gid=" + mysql.escape(gameID);
console.log(sql);
pool.getConnection(function (err, conn) {
conn.query(sql, function (err, results, fields) {
conn.release();
if (err) {
throw err;
}
resolve(results);
});
});
} catch (ex) {
logger.error(ex);
reject(ex);
}
});
}
exports.getGameList = getGameList;
exports.getGameServerList = getGameServerList;
================================================
FILE: hall_svr/hall-handler.js
================================================
const cmdDefine = require('../protocol/cmd-define');
const dbOper = require('./db-oper');
const packet = require('../protocol/packet');
const logger = require('./logger');
async function process(socket,data) {
let jObj = JSON.parse(data);
console.log(jObj);
let mainCmd = jObj.head.mcmd;
let subCmd = jObj.head.scmd;
switch (subCmd) {
case cmdDefine.SUB_HALL_GAME_LIST:
let gameList = await dbOper.getGameList();
let retObj = packet.getPacket(mainCmd, subCmd+100);
retObj.body = gameList;
let json = JSON.stringify(retObj);
socket.write(json);
break;
default:
logger.error("unsupport mainCmd:%d,subCmd:%d", mainCmd,subCmd);
break
}
}
exports.process = process;
================================================
FILE: hall_svr/hall-server.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
const process = require('process');
const hallHandler = require('./hall-handler');
const server = net.createServer();
function startServer() {
server.on('listening', function () {
logger.info("server is listening on port:%d", sysConfig.svrPort);
});
server.on('connection', function (socket) {
socket.setTimeout(60 * 1000, function () {
logger.error("ip:%s,idle timeout, disconncting, bye", socket.remoteAddress);
socket.end('idle timeout, disconnecting, bye!');
socket.destroy();
});
socket.on('data', function (data) {
logger.info("ip:%s, get data", socket.remoteAddress);
console.debug(data);
hallHandler.process(socket, data);
});
socket.on('error', function (err) {
logger.info("ip:%s,error", socket.remoteAddress);
logger.error(err);
socket.destroy();
});
socket.on('close', function (had_error) {
logger.info("ip:%s,socket closed", socket.remoteAddress);
if (!socket.destroyed) {
logger.info("destroy socket");
socket.destroy();
}
})
});
server.on('error', (err) => {
logger.error(err);
process.exit(1);
});
server.listen(sysConfig.svrPort, sysConfig.svrHost, () => {
logger.info('server bound on host:%s,port:%d', sysConfig.svrHost, sysConfig.svrPort);
});
}
exports.startServer = startServer;
================================================
FILE: hall_svr/hall.js
================================================
// 大厅提供以下功能:
// 游戏列表(游戏类型,游戏种类)
// 服务器列表
// 在线人数等其它信息
const hallServer = require('./hall-server');
const registerCenter = require('./register-center');
setInterval(registerCenter.registerSelf, 5*1000);
hallServer.startServer();
================================================
FILE: hall_svr/logger.js
================================================
const config = require('./config/log4js.json');
const log4js = require('log4js');
log4js.configure(config);
module.exports = log4js.getLogger();
================================================
FILE: hall_svr/register-center.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const packet = require('../protocol/packet');
const cmdDefine = require('../protocol/cmd-define');
const constDefine = require('../lib/const-define');
const shortID = require('./short-ID');
const logger = require('./logger');
let conn = null;
function registerSelf() {
let port = sysConfig.centerSvrPort;
let host = sysConfig.centerSvrHost;
console.log("connec to host:%s,port:%d", host, port);
conn = net.createConnection({port:port,host:host}, function () {
const psudoID = shortID.getNextID();
conn.psudoID = psudoID;
conn.ip = host;
let jObj = packet.getPacket(cmdDefine.CENTER, cmdDefine.SUB_CENTER_UPDATE);
let serverInfo = {};
serverInfo.type = constDefine.SERVER_TYPE_HALL;
serverInfo.port = sysConfig.svrPort;
serverInfo.name = "login_svr";
jObj.body = serverInfo;
let json = JSON.stringify(jObj);
console.log(json);
conn.write(json);
conn.destroy();
});
conn.on('end', function () {
logger.info("server connection closed,fd:%d,ip:%s", conn.psudoID, conn.ip);
});
conn.on('error', function (err) {
logger.info("server connection error,fd:%d,ip:%s", conn.psudoID, conn.ip);
logger.error(err);
});
conn.on('data', function (data) {
console.log("server fd:%d get data:%s", conn.psudoID, data);
});
}
exports.registerSelf = registerSelf;
================================================
FILE: hall_svr/short-ID.js
================================================
var idx = 1024;
function getNextID() {
if(idx > 4200000000) {
idx = 1024;
}
idx++;
return idx;
}
exports.getNextID = getNextID;
================================================
FILE: lib/black-ip.js
================================================
//ip黑名单
================================================
FILE: lib/const-define.js
================================================
module.exports = {
SERVER_TYPE_LOGIN : 2,
SERVER_TYPE_CENTER : 1,
SERVER_TYPE_GATE : 3,
SERVER_TYPE_HALL : 4,
SERVER_TYPE_GAME : 5
}
================================================
FILE: lib/rds-key.js
================================================
module.exports = {
KEY_SERVER_TYPE : "server_type",
SERVER_TYPE_LIST : [1,2,3],
}
================================================
FILE: login_svr/config/log4js.json
================================================
{
"appenders": {
"logfile": {
"type": "file",
"filename": "log/login.log",
"maxLogSize": 10485760,
"numBackups": 3
},
"console": {
"type": "console",
"level": "debug"
}
},
"categories": {
"default": {
"appenders": [
"console",
"logfile"
],
"level": "debug"
}
}
}
================================================
FILE: login_svr/config/sys-config.json
================================================
{
"svrHost":"127.0.0.1",
"svrPort":9100,
"centerSvrHost":"127.0.0.1",
"centerSvrPort":9200,
"redisHost":"127.0.0.1",
"redisPort":6379,
"redisPassword":"",
"mysqlHost":"127.0.0.1",
"mysqlPort":3306,
"mysqlUsername":"root",
"mysqlPassword":"123456"
}
================================================
FILE: login_svr/db-oper.js
================================================
const mysql = require('mysql');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
var pool = null;
function getPool() {
pool = mysql.createPool(
{
connectionLimit: 50, //TODO config in file
host: sysConfig.mysqlHost,
port: sysConfig.mysqlPort,
user: sysConfig.mysqlUsername,
password: sysConfig.mysqlPassword,
database: 'userdb'
}
);
}
getPool();
function login(userInfo) {
return new Promise(function (resolve, reject) {
try {
//avoid sql injection
let sql = 'select * from user_base where account=' + mysql.escape(userInfo.account) +
' and password=' + mysql.escape(userInfo.password);
console.log(sql);
pool.getConnection(function (err, conn) {
conn.query(sql, function (err, results, fields) {
conn.release();
if (err) {
throw err;
}
resolve(results);
});
});
} catch (ex) {
logger.error(ex);
reject(ex);
}
});
}
function register(userInfo) {
return new Promise(function (resolve, reject) {
try {
let sql = 'insert into user_base(accuounts, password) values(' +
mysq.escape(userInfo.accounts) + ',' + mysq.escape(userInfo.password) + ')';
console.log(sql);
pool.getConnection(function (err, conn) {
conn.query(sql, function (err, results, fields) {
conn.release();
if(err) {
throw ex;
}
resolve(results);
})
})
} catch (ex) {
logger.error(ex);
reject(ex);
}
});
}
exports.login = login;
exports.register = register;
================================================
FILE: login_svr/logger.js
================================================
const config = require('./config/log4js.json');
const log4js = require('log4js');
log4js.configure(config);
module.exports = log4js.getLogger();
================================================
FILE: login_svr/login-handler.js
================================================
const cmdDefine = require('../protocol/cmd-define');
const dbOper = require('./db-oper');
const packet = require('../protocol/packet');
async function process(socket, jObj) {
const mainCmd = jObj.head.mcmd;
const subCmd = jObj.head.scmd;
switch (subCmd) {
case cmdDefine.SUB_LOGIN_ACCOUNT:
let data = await dbOper.login(userInfo);
if (data.length > 0) {
data.token = "just for test";
var retObj = packet.getPacket(mainCmd,subCmd+100);
retObj.body = data;
socket.write(JSON.stringify(retObj));
}
break;
case cmdDefine.SUB_LOGIN_PHONE:
break;
case cmdDefine.SUB_LOGIN_VISITOR:
break;
}
}
================================================
FILE: login_svr/login.js
================================================
server = require('./server');
const registerCenter = require('./register-center');
const logger = require('./logger');
const sysConfig = require('./config/sys-config.json');
logger.debug(sysConfig);
setInterval(registerCenter.registerSelf, 5*1000);
server.startServer();
================================================
FILE: login_svr/register-center.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const packet = require('../protocol/packet');
const cmdDefine = require('../protocol/cmd-define');
const constDefine = require('../lib/const-define');
const shortID = require('./short-ID');
const logger = require('./logger');
let conn = null;
function registerSelf() {
let port = sysConfig.centerSvrPort;
let host = sysConfig.centerSvrHost;
console.log("connec to host:%s,port:%d", host, port);
conn = net.createConnection({port:port,host:host}, function () {
const psudoID = shortID.getNextID();
conn.psudoID = psudoID;
conn.ip = host;
let jObj = packet.getPacket(cmdDefine.CENTER, cmdDefine.SUB_CENTER_UPDATE);
let serverInfo = {};
serverInfo.type = constDefine.SERVER_TYPE_LOGIN;
serverInfo.port = sysConfig.svrPort;
serverInfo.name = "login_svr";
jObj.body = serverInfo;
let json = JSON.stringify(jObj);
console.log(json);
conn.write(json);
conn.destroy();
});
conn.on('end', function () {
logger.info("server connection closed,fd:%d,ip:%s", conn.psudoID, conn.ip);
});
conn.on('error', function (err) {
logger.info("server connection error,fd:%d,ip:%s", conn.psudoID, conn.ip);
logger.error(err);
});
conn.on('data', function (data) {
console.log("server fd:%d get data:%s", conn.psudoID, data);
});
}
exports.registerSelf = registerSelf;
================================================
FILE: login_svr/server.js
================================================
const net = require('net');
const sysConfig = require('./config/sys-config.json');
const logger = require('./logger');
const process = require('process');
const loginHandler = require('./login-handler');
const server = net.createServer();
function startServer() {
server.on('listening', function () {
logger.info("server is listening on port:%d", sysConfig.svrPort);
});
server.on('connection', function (socket) {
socket.setTimeout(60 * 1000, function () {
logger.error("ip:%s,idle timeout, disconncting, bye", socket.remoteAddress);
socket.end('idle timeout, disconnecting, bye!');
socket.destroy();
});
socket.on('data', function (data) {
logger.info("ip:%s, get data", socket.remoteAddress);
console.debug(data);
loginHandler.process(socket, data);
});
socket.on('error', function (err) {
logger.info("ip:%s,error", socket.remoteAddress);
logger.error(err);
socket.destroy();
});
socket.on('close', function (had_error) {
logger.info("ip:%s,socket closed", socket.remoteAddress);
if (!socket.destroyed) {
logger.info("destroy socket");
socket.destroy();
}
})
});
server.on('error', (err) => {
logger.error(err);
process.exit(1);
});
server.listen(sysConfig.svrPort, sysConfig.svrHost, () => {
logger.info('server bound on host:%s,port:%d', sysConfig.svrHost, sysConfig.svrPort);
});
}
exports.startServer = startServer;
================================================
FILE: login_svr/short-ID.js
================================================
var idx = 1024;
function getNextID() {
if(idx > 4200000000) {
idx = 1024;
}
idx++;
return idx;
}
exports.getNextID = getNextID;
================================================
FILE: package.json
================================================
{
"name": "gamex",
"version": "1.0.0",
"description": "gamex",
"main": "null.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"gamex"
],
"author": "shp",
"license": "ISC",
"dependencies": {
"express": "^4.16.2",
"log4js": "^2.3.5",
"mysql": "^2.15.0",
"net": "^1.0.2",
"process": "^0.11.10",
"redis": "^2.8.0",
"redis-pool-connection": "^1.4.0",
"ws": "^3.2.0"
}
}
================================================
FILE: protocol/client-protocol.md
================================================
本文件包含客户端与服务端交互协议
================================================
FILE: protocol/cmd-define.js
================================================
/*
1-1000 // framework
1001-1200 // login server
1201-1300 // center server
1301-1400 // hall server
3000-10000 // game server
10000-20000 //client <-> server
//packet:{
// "head":{
// "mcmd":1,
// "scmd:":1,
// "remoteAddress":"127.0.0.1",
// "seqNo":0
// },
// "body": {
// //as your wish
// }
//}
center server
//
// serverInfo:{
// name
// type
// ip
// port
// activeTime
// serverNo
// ext
// }
*/
module.exports = {
HEART_BEAT:1,
LOGIN:1001,
SUB_LOGIN_ACCOUNT:1, //帐号登录
SUB_LOGIN_PHONE:2, //手机登录
SUB_LOGIN_VISITOR:3, //游客登录
CENTER:1201,
SUB_CENTER_UPDATE:1, //向中央服务器上报状态
SUB_CENTER_GET:2, //从中央服务器获取对应的服务列表
SUB_CENTER_PLAYER_OL, //玩家登陆,玩家退出游戏也是这个状态
SUB_CENTER_PLAYER_PL, //玩家游戏中
HALL:1301,
SUB_HALL_GAME_LIST:1, //游戏列表
SUB_GAME_SERVER_LIST:2, //游戏服务列表
GAME:1401,
SUB_GAME_CREATE_ROOM:1, //创建房间
SUB_GAME_ENTER_ROOM:2, //进入房间
SUB_GAME_LEAVE_ROOM:3, //退出房间
SUB_GAME_DISMISS_ROOM:4, //解散房间
SUB_GAME_DESK_SIT_DOWN:5, //坐下
SUB_GAME_DESK_READY:6, //准备
SUB_GAME_DESK_STAND_UP:7, //起立
SUB_GAME_DESK_OPE_CARD:8, //操作牌
SUB_GAME_DESK_WIN:9, //胡牌
SUB_GAME_REENTER_ROOM:10, //断线重进房间
SUB_GAME_QUERY_PLAYER_INFO:11, //查询玩家游戏状态
SUB_GAME_DESK_WATCH:12, //观战
SUB_GAME_KICK_PLAYER:1, //房主踢人
EVENT_OUT_CARD:1,
EVENT_PASS_CARD:2,
};
================================================
FILE: protocol/packet.js
================================================
function Packet(mainCmd,subCmd) {
this.head = {};
this.head.mcmd = mainCmd;
this.head.scmd = subCmd;
this.body = {};
}
function getPacket(mainCmd,subCmd) {
return new Packet(mainCmd,subCmd);
}
exports.getPacket = getPacket;
================================================
FILE: protocol/server-server-protocol.md
================================================
本文件包含服务器内部协议定义
================================================
FILE: sql/gamedb.sql
================================================
create database if not exists gamedb default charset utf8;
use gamedb;
-- 游戏表
-- 游戏id手工维护
create table if not exists game_list (
gid int not null default '0' comment '游戏ID',
name varchar(32) not null default '' comment '游戏名称',
iconURL varchar(255) not null default '' comment '游戏图标',
index(gid)
) engine=innodb default charset 'utf8';
-- 游戏服相关信息
-- 注意游戏服ip和端口均为外网ip和端口
create table if not exists game_server_list (
gid int not null default '0' comment '游戏ID',
name varchar(64) not null default '' comment '游戏服名称',
ip char(16) not null default '' comment '游戏服ip',
port int not null default '0' comment '游戏服端口',
lowScore bigint not null default '0' comment '允许最低分,为0表示不限制',
upScore bigint not null default '0' comment '允许最高分,为0表示不限制',
status int not null default '0' comment '1为正常,2为维护中,3为停服',
maxRoom int not null default '100' comment '最大房间数',
createTime timestamp not null default 0 comment '创建时间',
modifyTime timestamp not null default 0 comment '修改时间',
index(gid,status)
) engine=innodb default charset 'utf8';
-- 每个用户在每个游戏中都有一份单独的数据,保证分数和金币数不乱
create table if not exists user_game_info (
uid int not null default '0' comment '用户id',
uname varchar(64) not null default '' comment '用户昵称',
gid int not null default '0' comment '游戏ID',
score bigint not null default '0' comment '用户得分',
coins bigint not null default '0' comment '用户金币数',
gems int not null default '0' comment '用户钻石数',
loginTime timestamp not null default 0 comment '登录时间',
logoutTime timestamp not null default 0 comment '退出时间',
index(uid,gid)
) engine = innodb default charset 'utf8';
================================================
FILE: sql/userdb.sql
================================================
create database if not exists userdb default charset utf8;
use userdb;
-- 用户表
-- 用户只能单点登陆,单点登陆在redis中实现,不写数据库,这个表应该是读多写少的
create table if not exists user_base (
uid int not null default 0 comment '用户ID',
account varchar(64) not null default '' comment '用户帐号',
type int not null default 0 comment '用户类型[渠道],1原始,2游客,3微信',
name varchar(64) not null default '' comment '用户名',
nickName varchar(64) not null default '' comment '昵称',
password char(32) not null default '' comment '用户密码',
token char(32) not null default '' comment '用户动态token',
sex int not null default 0 comment '性别',
city int not null default 0 comment '城市',
province int not null default 0 comment '省份',
faceURL char(255) not null default '' comment '用户头像',
createTime timestamp not null default 0 comment '创建时间',
index(account,password)
) engine=innodb default charset 'utf8';
gitextract_bpk30wdz/
├── .idea/
│ └── vcs.xml
├── README.md
├── center_svr/
│ ├── README.md
│ ├── center-handler.js
│ ├── center-server.js
│ ├── center.js
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── logger.js
│ ├── redis-oper.js
│ └── short-ID.js
├── game_svr/
│ ├── README.md
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── desk.js
│ ├── game-handler.js
│ ├── game-server.js
│ ├── game.js
│ ├── plugin/
│ │ ├── ddz-rule.js
│ │ └── ddz.js
│ ├── register-center.js
│ ├── room-list.js
│ └── room.js
├── gate_svr/
│ ├── README.md
│ ├── client-handler.js
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── connections.js
│ ├── gate.js
│ ├── logger.js
│ ├── redis-oper.js
│ ├── register-center.js
│ ├── server-handler.js
│ ├── short-ID.js
│ ├── sys-cache.js
│ ├── tcp-client.js
│ └── ws-server.js
├── hall_svr/
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── db-oper.js
│ ├── hall-handler.js
│ ├── hall-server.js
│ ├── hall.js
│ ├── logger.js
│ ├── register-center.js
│ └── short-ID.js
├── lib/
│ ├── black-ip.js
│ ├── const-define.js
│ └── rds-key.js
├── login_svr/
│ ├── config/
│ │ ├── log4js.json
│ │ └── sys-config.json
│ ├── db-oper.js
│ ├── logger.js
│ ├── login-handler.js
│ ├── login.js
│ ├── register-center.js
│ ├── server.js
│ └── short-ID.js
├── package.json
├── protocol/
│ ├── client-protocol.md
│ ├── cmd-define.js
│ ├── packet.js
│ └── server-server-protocol.md
└── sql/
├── gamedb.sql
└── userdb.sql
SYMBOL INDEX (113 symbols across 34 files)
FILE: center_svr/center-handler.js
function registerServer (line 24) | function registerServer(serverInfo) {
function removeServer (line 29) | function removeServer() {
function getServer (line 34) | async function getServer(socket, serverInfo) {
function process (line 39) | function process(socket, data) {
FILE: center_svr/center-server.js
function startServer (line 13) | function startServer() {
FILE: center_svr/redis-oper.js
function getConnection (line 17) | function getConnection() {
function saveServer (line 32) | function saveServer(serverInfo) {
function getServerList (line 102) | function getServerList(type) {
function removeServer (line 117) | async function removeServer(serverInfo) {
FILE: center_svr/short-ID.js
function getNextID (line 4) | function getNextID() {
FILE: game_svr/desk.js
function init (line 6) | function init() {
function playerOpeCard (line 10) | function playerOpeCard(socket, packet) {
FILE: game_svr/game-handler.js
function process (line 7) | function process(socket, data) {
FILE: game_svr/game-server.js
function startServer (line 9) | function startServer() {
FILE: game_svr/plugin/ddz-rule.js
constant SUIT_TYPE (line 15) | let SUIT_TYPE = {
constant CARD_CHAR (line 24) | let CARD_CHAR = [
constant CARD_TYPE (line 29) | let CARD_TYPE = {
function getSuitType (line 49) | function getSuitType(id) {
function getCardChar (line 68) | function getCardChar(id) {
function getGrade (line 85) | function getGrade(id) {
function sortCards (line 119) | function sortCards(cards) {
function getGrades (line 127) | function getGrades(cards) {
function dump (line 140) | function dump(cards) {
function isDan (line 149) | function isDan(cards) {
function isDuiZi (line 158) | function isDuiZi(cards) {
function isDuiWang (line 167) | function isDuiWang(cards) {
function isSan (line 177) | function isSan(cards) {
function isSanDaiYi (line 187) | function isSanDaiYi(cards) {
function isSanDaiDui (line 213) | function isSanDaiDui(cards) {
function isSi (line 246) | function isSi(cards) {
function isSiDaiEr (line 263) | function isSiDaiEr(cards) {
function isSiDaiLiangDui (line 281) | function isSiDaiLiangDui(cards) {
function isShunZi (line 316) | function isShunZi(cards) {
function isLianDui (line 343) | function isLianDui(cards)
function isFeiJi (line 379) | function isFeiJi(cards) {
function isFeiJiDaiDan (line 427) | function isFeiJiDaiDan(cards) {
function isFeiJiDaiDui (line 468) | function isFeiJiDaiDui(cards) {
function getCardType (line 508) | function getCardType(cards) {
function getCardDetail (line 603) | function getCardDetail(card) {
function getSortedCardsDetail (line 609) | function getSortedCardsDetail(cards) {
function checkCards (line 620) | function checkCards(preCards, curCards) {
function getAllCards (line 659) | function getAllCards() {
FILE: game_svr/plugin/ddz.js
class DouDiZhu (line 4) | class DouDiZhu {
method constructor (line 6) | constructor() {
method initGame (line 18) | initGame() {
method sendCards (line 74) | sendCards() {
method outCards (line 79) | outCards() {
method passCards (line 84) | passCards() {
method qiangDiZhu (line 90) | qiangDiZhu() {
FILE: game_svr/register-center.js
function registerSelf (line 12) | function registerSelf() {
FILE: game_svr/room-list.js
function initRoomList (line 19) | function initRoomList(gid,maxRoomCount) {
function getRoom (line 37) | function getRoom() {
function destroyRoom (line 53) | function destroyRoom(roomId) {
FILE: game_svr/room.js
function createRoom (line 9) | function createRoom(socket, packet) {
function enterRoom (line 14) | function enterRoom(socket, packet) {
function leaveRoom (line 19) | function leaveRoom(socket, packet) {
function dismissRoom (line 24) | function dismissRoom(socket, packet) {
function reenterRoom (line 29) | function reenterRoom(socket, packet) {
function queryPlayInfo (line 34) | function queryPlayInfo(socket, packet) {
function playerSitDown (line 39) | function playerSitDown(socket, packet) {
function playerReady (line 44) | function playerReady(socket, packet) {
function playerStandUp (line 49) | function playerStandUp(socket, packet) {
function playerWatch (line 54) | function playerWatch(socket, packet) {
function playerOpeCard (line 59) | function playerOpeCard(socket, packet) {
function playerWin (line 64) | function playerWin(socket, packet) {
function kickPlayer (line 69) | function kickPlayer(socket, packet) {
FILE: gate_svr/client-handler.js
function onConnection (line 13) | function onConnection(fd,ws) {
function onMessage (line 18) | function onMessage(ws,msg) {
function onError (line 38) | function onError(ws,err) {
function onClose (line 45) | function onClose(ws) {
FILE: gate_svr/connections.js
function addClientConn (line 6) | function addClientConn(fd,conn) {
function updateClientConn (line 10) | function updateClientConn(conn) {
function addSvrConn (line 14) | function addSvrConn(fd, conn) {
function addMapConn (line 18) | function addMapConn(fdClient, fdSvr) {
function updateSvrConn (line 23) | function updateSvrConn(conn) {
function getClientConn (line 27) | function getClientConn(fd) {
function getSvrConn (line 34) | function getSvrConn(fd) {
function getMapConn (line 41) | function getMapConn(fd) {
function removeSvrConn (line 48) | function removeSvrConn(fd) {
function removeClientConn (line 64) | function removeClientConn(fd) {
function checkTimeout (line 80) | function checkTimeout() {
FILE: gate_svr/redis-oper.js
function readCachedServers (line 10) | function readCachedServers(cb) {
FILE: gate_svr/register-center.js
function registerSelf (line 12) | function registerSelf() {
FILE: gate_svr/server-handler.js
function onConnection (line 11) | function onConnection(fd,tcpConn) {
function onMessage (line 16) | function onMessage(tcpConn,msg) {
function onError (line 23) | function onError(tcpConn,err) {
function onClose (line 30) | function onClose(tcpConn) {
FILE: gate_svr/short-ID.js
function getNextID (line 4) | function getNextID() {
FILE: gate_svr/sys-cache.js
function saveServerInfo (line 6) | function saveServerInfo(type,serverList) {
function readCached (line 12) | function readCached() {
function getServerList (line 17) | function getServerList(type) {
FILE: gate_svr/tcp-client.js
function getNewConnection (line 9) | function getNewConnection(port,host) {
FILE: gate_svr/ws-server.js
function startServer (line 8) | function startServer(sysConfig) {
FILE: hall_svr/db-oper.js
function getPool (line 8) | function getPool() {
function getGameList (line 23) | function getGameList() {
function getGameServerList (line 50) | function getGameServerList(gameID) {
FILE: hall_svr/hall-handler.js
function process (line 7) | async function process(socket,data) {
FILE: hall_svr/hall-server.js
function startServer (line 9) | function startServer() {
FILE: hall_svr/register-center.js
function registerSelf (line 12) | function registerSelf() {
FILE: hall_svr/short-ID.js
function getNextID (line 4) | function getNextID() {
FILE: login_svr/db-oper.js
function getPool (line 8) | function getPool() {
function login (line 23) | function login(userInfo) {
function register (line 51) | function register(userInfo) {
FILE: login_svr/login-handler.js
function process (line 5) | async function process(socket, jObj) {
FILE: login_svr/register-center.js
function registerSelf (line 12) | function registerSelf() {
FILE: login_svr/server.js
function startServer (line 9) | function startServer() {
FILE: login_svr/short-ID.js
function getNextID (line 4) | function getNextID() {
FILE: protocol/packet.js
function Packet (line 2) | function Packet(mainCmd,subCmd) {
function getPacket (line 10) | function getPacket(mainCmd,subCmd) {
FILE: sql/gamedb.sql
type game_list (line 8) | create table if not exists game_list (
type game_server_list (line 18) | create table if not exists game_server_list (
type user_game_info (line 34) | create table if not exists user_game_info (
FILE: sql/userdb.sql
type user_base (line 8) | create table if not exists user_base (
Condensed preview — 65 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (81K chars).
[
{
"path": ".idea/vcs.xml",
"chars": 180,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n <component name=\"VcsDirectoryMappings\">\n <mapping dire"
},
{
"path": "README.md",
"chars": 711,
"preview": "# gamex\n棋牌类游戏框架\n\n技术基于node.js,写这个东西是因为最近接触了一部分棋牌的代码,过程实在是不那么让人愉快,代码规范性,可读性,稳定性,扩展性都不好。于是想用一个能够快速开发的工具来实现一个自己的框架。\n\ncenter_"
},
{
"path": "center_svr/README.md",
"chars": 228,
"preview": "\n中央服务器\n\n功能:服务注册,服务查询\n每一类服务器都有一个单独编号,编号是递增的。如果服务停掉了,这个编号可能会被下一个启动的服务使用。\n每个服务都可以带有扩展信息,但是不建议使用,因为一旦添加了扩展信息就意味着中央服务器要修改,中央服"
},
{
"path": "center_svr/center-handler.js",
"chars": 1476,
"preview": "\n\n//-------------------------------------\n// redis\n// type : server1 server2 server3\n//\n//\n//-----------------"
},
{
"path": "center_svr/center-server.js",
"chars": 1786,
"preview": "const net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require('./logger');\nc"
},
{
"path": "center_svr/center.js",
"chars": 72,
"preview": "\ncenterServer = require('./center-server');\n\ncenterServer.startServer();"
},
{
"path": "center_svr/config/log4js.json",
"chars": 368,
"preview": "{\n \"appenders\": {\n \"logfile\": {\n \"type\": \"file\",\n \"filename\": \"log/center.log\",\n \"maxLogSize\": 104857"
},
{
"path": "center_svr/config/sys-config.json",
"chars": 115,
"preview": "\n{\n \"svrHost\":\"127.0.0.1\",\n \"svrPort\":9200,\n \"redisHost\":\"127.0.0.1\",\n \"redisPort\":6379,\n \"redisPassword\":\"\"\n}"
},
{
"path": "center_svr/logger.js",
"chars": 147,
"preview": "\nconst config = require('./config/log4js.json');\n\nconst log4js = require('log4js');\nlog4js.configure(config);\n\nmodule.ex"
},
{
"path": "center_svr/redis-oper.js",
"chars": 3178,
"preview": "\n//\n// 将服务信息保存在redis中,如果服务比较多,会造成冲突。每5秒一次心跳,极端情况下可能有服务永远注册不上。\n// 仅有几个服务甚至十几个服务可以忽略上面的问题。有几十个服务就不要使用这种方式了,应该用rpc。\n//\n\ncon"
},
{
"path": "center_svr/short-ID.js",
"chars": 157,
"preview": "\nvar idx = 1024;\n\n function getNextID() {\n if(idx > 4200000000) {\n idx = 1024;\n }\n idx++;\n return idx"
},
{
"path": "game_svr/README.md",
"chars": 250,
"preview": "\n游戏服务器可以水平扩展。\n游戏通过插件方式写在plugin目录下,同时将插件信息配置到config/sys-config中。\n每个游戏一到多个游戏服务器,但不支持一个游戏服务器跑多个游戏。\n每个游戏服务器有多个房间列表,房间相关配置信息在"
},
{
"path": "game_svr/config/log4js.json",
"chars": 366,
"preview": "{\n \"appenders\": {\n \"logfile\": {\n \"type\": \"file\",\n \"filename\": \"log/game.log\",\n \"maxLogSize\": 10485760"
},
{
"path": "game_svr/config/sys-config.json",
"chars": 191,
"preview": "\n{\n \"svrHost\":\"127.0.0.1\",\n \"svrPort\":9000,\n \"redisHost\":\"127.0.0.1\",\n \"redisPort\":6379,\n \"redisPassword\":\"\",\n \"ce"
},
{
"path": "game_svr/desk.js",
"chars": 547,
"preview": "\n\nconst sysConfig = require('./config/sys-config.json');\nconst cmdDefine = require('../protocol/cmd-define');\n\nfunction "
},
{
"path": "game_svr/game-handler.js",
"chars": 1993,
"preview": "\n\nconst packet = require('../protocol/packet');\nconst cmdDefine = require('../protocol/cmd-define');\nconst room = requir"
},
{
"path": "game_svr/game-server.js",
"chars": 1635,
"preview": "const net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require('./logger');\nc"
},
{
"path": "game_svr/game.js",
"chars": 260,
"preview": "\n\nconst registerCenter = require('./register-center');\nconst gameServer = require('./game-server');\nconst sysConfig = re"
},
{
"path": "game_svr/plugin/ddz-rule.js",
"chars": 17506,
"preview": "/*\n 牌列表说明:\n每个牌的每个花色都有一个单独的ID,一共54个ID,ID按黑红梅方的顺序生成\na,2,3,....q,k 黑桃 [1,2,3,4,5,6,7,8,9,10,11,12,13]\na,2,3,....q,k 红桃 ["
},
{
"path": "game_svr/plugin/ddz.js",
"chars": 1955,
"preview": "\nconst ddzRule = require('./ddz-rule');\n\nclass DouDiZhu {\n\n constructor() {\n //牌池\n this.cardPool = [];\n"
},
{
"path": "game_svr/register-center.js",
"chars": 1522,
"preview": "\nconst net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst packet = require('../protocol/"
},
{
"path": "game_svr/room-list.js",
"chars": 992,
"preview": "\n//\n// 房间号生成规则:游戏ID+6个数字,6个数字随机生成,每次服务重启生成的数字都不一样\n// 避免被猜到房间号,每个房间号带一个4位随机码,获取房间的时候动态生成\n//\n\n//\n// [1,2,3,4,5] 方便随机数获取\n//"
},
{
"path": "game_svr/room.js",
"chars": 1345,
"preview": "\n// room - desk\n\n//房间列表暂时不存数据库了\n\nconst desk = require('./desk');\n\n//创建一个房间,实际是从已有房间里取一个\nfunction createRoom(socket, pack"
},
{
"path": "gate_svr/README.md",
"chars": 446,
"preview": "\ngate服务的设计原则\n\ngate服务按固定时间间隔上报状态到center服务器\ngate服务按固定时间间隔获取所有类型服务器列表(列表不能过超过1M大小,超过了redis会有卡顿现象)\ngate服务器在玩家登陆请求时转发请求到登陆服务器"
},
{
"path": "gate_svr/client-handler.js",
"chars": 1094,
"preview": "\n\n//\n//\n// 客户端请求进来时的处理\n//\n//\n\nconst logger = require('./logger');\nconst connections = require('./connections');\nconst cm"
},
{
"path": "gate_svr/config/log4js.json",
"chars": 366,
"preview": "{\n \"appenders\": {\n \"logfile\": {\n \"type\": \"file\",\n \"filename\": \"log/gate.log\",\n \"maxLogSize\": 10485760"
},
{
"path": "gate_svr/config/sys-config.json",
"chars": 170,
"preview": "\n{\n \"svrHost\":\"127.0.0.1\",\n \"svrPort\":9000,\n \"redisHost\":\"127.0.0.1\",\n \"redisPort\":6379,\n \"redisPassword\":\"\",\n \"ce"
},
{
"path": "gate_svr/connections.js",
"chars": 3405,
"preview": "\nvar clientConns = {};\nvar svrConns = {};\nvar mapConns = {};\n\nfunction addClientConn(fd,conn) {\n clientConns[fd"
},
{
"path": "gate_svr/gate.js",
"chars": 360,
"preview": "\nconst wsServer = require('./ws-server');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require("
},
{
"path": "gate_svr/logger.js",
"chars": 147,
"preview": "\nconst config = require('./config/log4js.json');\n\nconst log4js = require('log4js');\nlog4js.configure(config);\n\nmodule.ex"
},
{
"path": "gate_svr/redis-oper.js",
"chars": 723,
"preview": "\nconst sysConfig = require('./config/sys-config.json');\nconst redis = require('redis');\nconst redisKey = require('../lib"
},
{
"path": "gate_svr/register-center.js",
"chars": 1522,
"preview": "\nconst net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst packet = require('../protocol/"
},
{
"path": "gate_svr/server-handler.js",
"chars": 771,
"preview": "\n//\n//\n// 服务器返回数据时的处理\n//\n//\n\nconst logger = require('./logger');\nconst connections = require('./connections');\n\nfunction"
},
{
"path": "gate_svr/short-ID.js",
"chars": 157,
"preview": "\nvar idx = 1024;\n\n function getNextID() {\n if(idx > 4200000000) {\n idx = 1024;\n }\n idx++;\n return idx"
},
{
"path": "gate_svr/sys-cache.js",
"chars": 452,
"preview": "\nconst redisOper = require('./redis-oper');\n\nvar cached = {};\n\nfunction saveServerInfo(type,serverList) {\n let jArray"
},
{
"path": "gate_svr/tcp-client.js",
"chars": 1211,
"preview": "\n\nconst net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst serverHandler = require('./se"
},
{
"path": "gate_svr/ws-server.js",
"chars": 856,
"preview": "\n\nconst WebSocket = require('ws');\nconst clientHandler = require('./client-handler');\nconst shortID = require('./short-I"
},
{
"path": "hall_svr/config/log4js.json",
"chars": 366,
"preview": "{\n \"appenders\": {\n \"logfile\": {\n \"type\": \"file\",\n \"filename\": \"log/hall.log\",\n \"maxLogSize\": 10485760"
},
{
"path": "hall_svr/config/sys-config.json",
"chars": 271,
"preview": "\n{\n \"svrHost\":\"127.0.0.1\",\n \"svrPort\":9300,\n \"centerSvrHost\":\"127.0.0.1\",\n \"centerSvrPort\":9200,\n \"redisHost\":\"127."
},
{
"path": "hall_svr/db-oper.js",
"chars": 1822,
"preview": "const mysql = require('mysql');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require('./logger'"
},
{
"path": "hall_svr/hall-handler.js",
"chars": 804,
"preview": "\nconst cmdDefine = require('../protocol/cmd-define');\nconst dbOper = require('./db-oper');\nconst packet = require('../pr"
},
{
"path": "hall_svr/hall-server.js",
"chars": 1635,
"preview": "const net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require('./logger');\nc"
},
{
"path": "hall_svr/hall.js",
"chars": 248,
"preview": "\n// 大厅提供以下功能:\n// 游戏列表(游戏类型,游戏种类)\n// 服务器列表\n// 在线人数等其它信息\n\n\nconst hallServer = require('./hall-server');\ncon"
},
{
"path": "hall_svr/logger.js",
"chars": 147,
"preview": "\nconst config = require('./config/log4js.json');\n\nconst log4js = require('log4js');\nlog4js.configure(config);\n\nmodule.ex"
},
{
"path": "hall_svr/register-center.js",
"chars": 1523,
"preview": "\nconst net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst packet = require('../protocol/"
},
{
"path": "hall_svr/short-ID.js",
"chars": 157,
"preview": "\nvar idx = 1024;\n\n function getNextID() {\n if(idx > 4200000000) {\n idx = 1024;\n }\n idx++;\n return idx"
},
{
"path": "lib/black-ip.js",
"chars": 8,
"preview": "\n//ip黑名单"
},
{
"path": "lib/const-define.js",
"chars": 195,
"preview": "\nmodule.exports = {\n SERVER_TYPE_LOGIN : 2,\n SERVER_TYPE_CENTER : 1,\n SERVER_TYPE_GATE : "
},
{
"path": "lib/rds-key.js",
"chars": 111,
"preview": "\nmodule.exports = {\n\n KEY_SERVER_TYPE : \"server_type\",\n SERVER_TYPE_LIST : [1,2,3],\n\n}"
},
{
"path": "login_svr/config/log4js.json",
"chars": 367,
"preview": "{\n \"appenders\": {\n \"logfile\": {\n \"type\": \"file\",\n \"filename\": \"log/login.log\",\n \"maxLogSize\": 1048576"
},
{
"path": "login_svr/config/sys-config.json",
"chars": 271,
"preview": "\n{\n \"svrHost\":\"127.0.0.1\",\n \"svrPort\":9100,\n \"centerSvrHost\":\"127.0.0.1\",\n \"centerSvrPort\":9200,\n \"redisHost\":\"127."
},
{
"path": "login_svr/db-oper.js",
"chars": 2002,
"preview": "const mysql = require('mysql');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require('./logger'"
},
{
"path": "login_svr/logger.js",
"chars": 147,
"preview": "\nconst config = require('./config/log4js.json');\n\nconst log4js = require('log4js');\nlog4js.configure(config);\n\nmodule.ex"
},
{
"path": "login_svr/login-handler.js",
"chars": 767,
"preview": "const cmdDefine = require('../protocol/cmd-define');\nconst dbOper = require('./db-oper');\nconst packet = require('../pro"
},
{
"path": "login_svr/login.js",
"chars": 275,
"preview": "\nserver = require('./server');\nconst registerCenter = require('./register-center');\nconst logger = require('./logger');\n"
},
{
"path": "login_svr/register-center.js",
"chars": 1524,
"preview": "\nconst net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst packet = require('../protocol/"
},
{
"path": "login_svr/server.js",
"chars": 1638,
"preview": "const net = require('net');\nconst sysConfig = require('./config/sys-config.json');\nconst logger = require('./logger');\nc"
},
{
"path": "login_svr/short-ID.js",
"chars": 157,
"preview": "\nvar idx = 1024;\n\n function getNextID() {\n if(idx > 4200000000) {\n idx = 1024;\n }\n idx++;\n return idx"
},
{
"path": "package.json",
"chars": 469,
"preview": "{\n \"name\": \"gamex\",\n \"version\": \"1.0.0\",\n \"description\": \"gamex\",\n \"main\": \"null.js\",\n \"scripts\": {\n \"test\": \"ec"
},
{
"path": "protocol/client-protocol.md",
"chars": 17,
"preview": "\n本文件包含客户端与服务端交互协议"
},
{
"path": "protocol/cmd-define.js",
"chars": 1637,
"preview": "\n/*\n\n1-1000 // framework\n\n1001-1200 // login server\n1201-1300 // center server\n1301-1400 // hall server\n3000-10000 // ga"
},
{
"path": "protocol/packet.js",
"chars": 248,
"preview": "\nfunction Packet(mainCmd,subCmd) {\n\n this.head = {};\n this.head.mcmd = mainCmd;\n this.head.scmd = subCmd;\n t"
},
{
"path": "protocol/server-server-protocol.md",
"chars": 15,
"preview": "\n本文件包含服务器内部协议定义"
},
{
"path": "sql/gamedb.sql",
"chars": 1926,
"preview": "\ncreate database if not exists gamedb default charset utf8;\n\nuse gamedb;\n\n-- 游戏表\n-- 游戏id手工维护\ncreate table if not exists "
},
{
"path": "sql/userdb.sql",
"chars": 1011,
"preview": "\ncreate database if not exists userdb default charset utf8;\n\nuse userdb;\n\n-- 用户表\n-- 用户只能单点登陆,单点登陆在redis中实现,不写数据库,这个表应该是读"
}
]
About this extraction
This page contains the full source code of the shihuaping/gamex GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 65 files (66.8 KB), approximately 22.0k tokens, and a symbol index with 113 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.