Commit 8c5a41cb authored by mercury233's avatar mercury233

merge

parent 87a452c6
# ignore
test*
jsconfig.json
coffeelint.json
.vscode/
password.json
config.*.json
node_modules/.bin/
node_modules/bunyan/
node_modules/request/
node_modules/underscore/
node_modules/underscore.string/
node_modules/sqlite3/
node_modules/moment/
/ygopro
/windbot
/decks/
/decks_save*
/node_modules/
test*
*.heapsnapshot
*.tmp
*.bak
*.log
Thumbs.db
ehthumbs.db
Desktop.ini
......
## ygopro-server
一个YGOPRO服务器,基于mycard代码修改
一个YGOPRO服务器。
现用于[YGOPRO 233服](http://mercury233.me/ygosrv233/)
现用于[mycard](https://mycard.moe/)
###支持功能
* Linux上运行
......@@ -11,29 +11,56 @@
* 广播消息
* 召唤台词
* 先行卡一键更新
* Windbot在线AI
###不支持功能
* 用户账号系统
* 在线AI
* 在线聊天室
###使用方法
* 安装修改后的mycard版ygopro服务端:https://github.com/mercury233/ygopro/tree/server
* `git clone https://github.com/mercury233/ygopro-server.git`
* `cd ygopro-server`
* `npm install`
* 修改`config.json`
* 可参考[wiki](https://github.com/mercury233/ygopro-server/wiki)安装
* 手动安装:
* 安装mycard版ygopro服务端:https://github.com/mycard/ygopro/tree/server
* `git clone https://github.com/mycard/ygopro-server.git`
* `cd ygopro-server`
* `npm install`
*`config.json`复制为`config.user.json`并进行修改
* `port`为你想要的端口
* `version`为ygopro的十进制版本号(例如,0x1336=4918)
* `ygopro_path`为ygopro服务端的相对路径
* `modules.stop`为文本时,表示服务器关闭
* `modules.TCG_banlist_id`为lflist中正在使用的TCG禁卡表的编号,0开始
* 更多选项参见wiki
* `node ygopro-server.js`即可运行
* 简易的控制台在http://mercury233.me/ygosrv233/dashboard.html (我没有开发给用户使用的大厅的打算。)
###高级功能
* 待补充说明
* 简易的先行卡更新控制台在http://mercury233.me/ygosrv233/pre-dashboard.html
###开发计划
* 重写全部代码,与SalvationServer合并,或作为分支版本
### 开发计划
* 重做CTOS和STOC部分
* 模块化附加功能
* 房名代码
* 随机对战
* 召唤台词
* WindBot
* 云录像
* 比赛模式
* 先行卡更新
* 用户账号系统和管理员账号系统
* 云录像更换存储方式
### TODO
* refactoring CTOS and STOC
* change features to modules
* room name parsing
* random duel
* summon dialogues
* WindBot
* cloud replay
* tournament mode
* expansions updater
* user and admin account system
* new database for cloud replay
## Install Docker
```bash
......
{
"port": 7911,
"ygopro_path": "ygopro",
"modules": {
"welcome": "YGOPRO Server",
"update": "请更新游戏版本,可在社区手动下载更新包",
"stop": false,
"tips": "http://mycard.moe/tips.json",
"dialogues": "http://mycard.moe/dialogues.json",
"redis_port": 6379,
"enable_websocket_roomlist": true,
"enable_random_duel": false,
"enable_halfway_watch": true,
"enable_TCG_as_default": false,
"enable_cloud_replay": false,
"enable_windbot": true,
"enable_websocket_roomlist": true,
"enable_deck_log": false,
"redis_port": 6379,
"windbot_port": 2399,
"spawn_windbot": true,
"mycard_auth": "https://ygobbs.com",
"post_start_watching": true,
"enable_TCG_as_default": false,
"hang_timeout": 90,
"tournament_mode": {
"enabled": false,
"deck_path": "./decks/",
"duel_log": [],
"password": "123456",
"port": 7933
},
"pre_util": {
"enabled": false,
"port": 7944,
"password": "123456",
"git_html_path": "../mercury233.github.io/",
"html_path": "../mercury233.github.io/ygosrv233/",
"html_filename": "pre.html",
"git_db_path": "../ygopro-unofficial/",
"db_path": "../ygopro-unofficial/unofficial/",
"html_img_rel_path": "pre/pics/",
"html_img_thumbnail": "thumbnail/",
"ygopro_path": "../ygopro-pre/",
"only_show_dbs": {
"news.cdb": true,
"pre-release.cdb": true
},
"html_gits": [
{
"name": "GitHub",
"push": ["push", "origin"]
},
{
"name": "Coding",
"push": ["push", "coding", "master:master"]
}
]
},
"http": {
"port": 7922,
"password": "123456",
......@@ -24,5 +62,14 @@
"key": "ssl/privkey.pem"
}
}
},
"ban": {
"banned_user": [],
"banned_ip": [],
"badword_level0": ["滚", "衮", "操", "草", "艹", "狗", "日", "曰", "妈", "娘", "逼"],
"badword_level1": ["傻逼", "鸡巴"],
"badword_level2": ["死妈", "草你妈"],
"badword_level3": ["迷奸", "仿真枪"],
"illegal_id": ["^Lv.\\d+", "^VIP.\\d+"]
}
}
{
"port": 2333,
"password": "123456",
"git_html_path": "../mercury233.github.io/",
"html_path": "../mercury233.github.io/ygosrv233/",
"html_filename": "pre.html",
"git_db_path": "../ygopro-cards/",
"db_path": "../ygopro-cards/unofficial/",
"html_img_rel_path": "pre/pics/",
"ygopro_path": "../ygopro-pre/"
}
\ No newline at end of file
......@@ -247,6 +247,18 @@
"ATTRIBUTE_LIGHT": 16,
"ATTRIBUTE_DARK": 32,
"ATTRIBUTE_DEVINE": 64
},
"COLORS": {
"LIGHTBLUE": 8,
"RED": 11,
"GREEN": 12,
"BLUE": 13,
"BABYBLUE": 14,
"PINK": 15,
"YELLOW": 16,
"WHITE": 17,
"GRAY": 18,
"DARKGRAY": 19
}
}
......
......@@ -10,14 +10,14 @@
],
"author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>",
"dependencies": {
"bunyan": "latest",
"moment": "latest",
"nconf": "latest",
"pg": "latest",
"request": "latest",
"underscore": "latest",
"underscore.string": "latest",
"request": "latest",
"moment": "latest",
"sqlite3": "latest",
"bunyan": "latest",
"ws": "latest",
"pg": "latest"
"ws": "latest"
},
"license": "AGPL-3.0",
"scripts": {
......
......@@ -20,6 +20,7 @@
"GAME_MSG": "GameMsg_Hint_Card_only",
"SELECT_HAND": "",
"SELECT_TP": "",
"REPLAY": "",
"CHAT": "STOC_Chat"
}
}
_ = require 'underscore'
_.str = require 'underscore.string'
_.mixin(_.str.exports());
spawn = require('child_process').spawn
spawnSync = require('child_process').spawnSync
settings = require './config.json'
ygopro = require './ygopro.js'
roomlist = require './roomlist' if settings.modules.enable_websocket_roomlist
bunyan = require 'bunyan'
moment = require 'moment'
#redis = require 'redis'
moment.locale('zh-cn', {
relativeTime: {
future: '%s内',
past: '%s前',
s: '%d秒',
m: '1分钟',
mm: '%d分钟',
h: '1小时',
hh: '%d小时',
d: '1天',
dd: '%d天',
M: '1个月',
MM: '%d个月',
y: '1年',
yy: '%d年'
}
})
log = bunyan.createLogger name: "mycard-room"
#redisdb = redis.createClient host: "127.0.0.1", port: settings.modules.redis_port
#获取可用内存
get_memory_usage = ()->
prc_free = spawnSync("free", [])
if (prc_free.stdout)
lines = prc_free.stdout.toString().split(/\n/g)
line = lines[1].split(/\s+/)
total = parseInt(line[1], 10)
free = parseInt(line[3], 10)
buffers = parseInt(line[5], 10)
cached = parseInt(line[6], 10)
actualFree = free + buffers + cached
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2))
else
percentUsed = 0
return percentUsed
class Room
#name
#port
#players: [client]
#process
#established
#alive
@all = []
@players_oppentlist = {}
@players_banned = []
@ban_player: (name, ip, reason)->
bannedplayer = _.find Room.players_banned, (bannedplayer)->
ip == bannedplayer.ip
if bannedplayer
bannedplayer.count = bannedplayer.count + 1
bantime = if bannedplayer.count > 3 then Math.pow(2, bannedplayer.count - 3) * 2 else 0
bannedplayer.time = if moment() < bannedplayer.time then moment(bannedplayer.time).add(bantime, 'm') else moment().add(bantime, 'm')
bannedplayer.reasons.push(reason) if not _.find bannedplayer.reasons, (bannedreason)->
bannedreason == reason
bannedplayer.need_tip = true;
else
bannedplayer = {"ip": ip, "time": moment(), "count": 1, "reasons": [reason], "need_tip": true}
Room.players_banned.push(bannedplayer)
log.info("banned", name, ip, reason, bannedplayer.count)
return
@find_or_create_by_name: (name, player_ip)->
if settings.modules.enable_random_duel and (name == '' or name.toUpperCase() == 'S' or name.toUpperCase() == 'M' or name.toUpperCase() == 'T')
return @find_or_create_random(name.toUpperCase(), player_ip)
if room = @find_by_name(name)
return room
else if get_memory_usage() >= 90
return null
else
return new Room(name)
@find_or_create_random: (type, player_ip)->
bannedplayer = _.find Room.players_banned, (bannedplayer)->
return player_ip == bannedplayer.ip
if bannedplayer
if bannedplayer.count > 6 and moment() < bannedplayer.time
return {"error": "因为您近期在游戏中多次#{bannedplayer.reasons.join('、')},您已被禁止使用随机对战功能,将在#{moment(bannedplayer.time).fromNow(true)}后解封"}
if bannedplayer.count > 3 and moment() < bannedplayer.time and bannedplayer.need_tip
bannedplayer.need_tip = false
return {"error": "因为您近期在游戏中#{bannedplayer.reasons.join('、')},在#{moment(bannedplayer.time).fromNow(true)}内您随机对战时只能遇到其他违规玩家"}
else if bannedplayer.need_tip
bannedplayer.need_tip = false
return {"error": "系统检测到您近期在游戏中#{bannedplayer.reasons.join('、')},若您违规超过3次,将受到惩罚"}
else if bannedplayer.count > 2
bannedplayer.need_tip = true
max_player = if type == 'T' then 4 else 2
playerbanned = (bannedplayer and bannedplayer.count > 3 and moment() < bannedplayer.time)
result = _.find @all, (room)->
return room.random_type != '' and !room.started and ((type == '' and room.random_type != 'T') or room.random_type == type) and room.get_playing_player().length < max_player and (room.get_host() == null or room.get_host().remoteAddress != Room.players_oppentlist[player_ip]) and (playerbanned == room.deprecated)
if result
result.welcome = '对手已经在等你了,开始决斗吧!'
#log.info 'found room', player_name
else
type = if type then type else 'S'
name = type + ',RANDOM#' + Math.floor(Math.random() * 100000)
result = new Room(name)
result.random_type = type
result.max_player = max_player
result.welcome = '已建立随机对战房间,正在等待对手!'
result.deprecated = playerbanned
#log.info 'create room', player_name, name
return result
@find_by_name: (name)->
result = _.find @all, (room)->
room.name == name
#log.info 'find_by_name', name, result
return result
@find_by_port: (port)->
_.find @all, (room)->
room.port == port
@validate: (name)->
client_name_and_pass = name.split('$', 2)
client_name = client_name_and_pass[0]
client_pass = client_name_and_pass[1]
return true if !client_pass
!_.find Room.all, (room)->
room_name_and_pass = room.name.split('$', 2)
room_name = room_name_and_pass[0]
room_pass = room_name_and_pass[1]
client_name == room_name and client_pass != room_pass
constructor: (name, @hostinfo) ->
@name = name
@alive = true
@players = []
@player_datas = []
@status = 'starting'
@started = false
@established = false
@watcher_buffers = []
@watchers = []
@random_type = ''
@welcome = ''
Room.all.push this
@hostinfo ||=
lflist: _.findIndex settings.lflist, (list)-> !list.tcg and list.date.isBefore()
rule: if settings.modules.enable_TCG_as_default then 2 else 0
mode: 0
enable_priority: false
no_check_deck: false
no_shuffle_deck: false
start_lp: 8000
start_hand: 5
draw_count: 1
time_limit: 180
if name[0...2] == 'M#'
@hostinfo.mode = 1
else if name[0...2] == 'T#'
@hostinfo.mode = 2
@hostinfo.start_lp = 16000
else if (param = name.match /^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i)
@hostinfo.rule = parseInt(param[1])
@hostinfo.mode = parseInt(param[2])
@hostinfo.enable_priority = param[3] == 'T'
@hostinfo.no_check_deck = param[4] == 'T'
@hostinfo.no_shuffle_deck = param[5] == 'T'
@hostinfo.start_lp = parseInt(param[6])
@hostinfo.start_hand = parseInt(param[7])
@hostinfo.draw_count = parseInt(param[8])
else if (((param = name.match /(.+)#/) != null) and ( (param[1].length <= 2 and param[1].match(/(S|N|M|T)(0|1|2|T|A)/i)) or (param[1].match(/^(S|N|M|T)(0|1|2|O|T|A)(0|1|O|T)/i)) ) )
rule = param[1].toUpperCase()
#log.info "C", rule
switch rule.charAt(0)
when "M","1"
@hostinfo.mode = 1
when "T","2"
@hostinfo.mode = 2
@hostinfo.start_lp = 16000
else
@hostinfo.mode = 0
switch rule.charAt(1)
when "0","O"
@hostinfo.rule = 0
when "1","T"
@hostinfo.rule = 1
else
@hostinfo.rule = 2
switch rule.charAt(2)
when "1","T"
@hostinfo.lflist = _.findIndex settings.lflist, (list)-> list.tcg and list.date.isBefore()
else
@hostinfo.lflist = _.findIndex settings.lflist, (list)-> !list.tcg and list.date.isBefore()
if ((param = parseInt(rule.charAt(3).match(/\d/))) >= 0)
@hostinfo.time_limit = param * 60
switch rule.charAt(4)
when "T","1"
@hostinfo.enable_priority = true
else
@hostinfo.enable_priority = false
switch rule.charAt(5)
when "T","1"
@hostinfo.no_check_deck = true
else
@hostinfo.no_check_deck = false
switch rule.charAt(6)
when "T","1"
@hostinfo.no_shuffle_deck = true
else
@hostinfo.no_shuffle_deck = false
if ((param = parseInt(rule.charAt(7).match(/\d/))) > 0)
@hostinfo.start_lp = param * 4000
if ((param = parseInt(rule.charAt(8).match(/\d/))) > 0)
@hostinfo.start_hand = param
if ((param = parseInt(rule.charAt(9).match(/\d/))) >= 0)
@hostinfo.draw_count = param
else if ((param = name.match /(.+)#/) != null)
rule = param[1].toUpperCase()
#log.info "233", rule
if (rule.match /(^|,|,)(M|MATCH)(,|,|$)/)
@hostinfo.mode = 1
if (rule.match /(^|,|,)(T|TAG)(,|,|$)/)
@hostinfo.mode = 2
@hostinfo.start_lp = 16000
if (rule.match /(^|,|,)(TCGONLY|TO)(,|,|$)/)
@hostinfo.rule = 1
@hostinfo.lflist = _.findIndex settings.lflist, (list)-> list.tcg and list.date.isBefore()
if (rule.match /(^|,|,)(OCGONLY|OO)(,|,|$)/)
@hostinfo.rule = 0
if (rule.match /(^|,|,)(OT|TCG)(,|,|$)/)
@hostinfo.rule = 2
if (param = rule.match /(^|,|,)LP(\d+)(,|,|$)/)
start_lp = parseInt(param[2])
if (start_lp <= 0) then start_lp = 1
if (start_lp >= 99999) then start_lp = 99999
@hostinfo.start_lp = start_lp
if (param = rule.match /(^|,|,)(TIME|TM|TI)(\d+)(,|,|$)/)
time_limit = parseInt(param[3])
if (time_limit < 0) then time_limit = 180
if (time_limit >= 1 and time_limit <= 60) then time_limit = time_limit * 60
if (time_limit >= 999) then time_limit = 999
@hostinfo.time_limit = time_limit
if (param = rule.match /(^|,|,)(START|ST)(\d+)(,|,|$)/)
start_hand = parseInt(param[3])
if (start_hand <= 0) then start_hand = 1
if (start_hand >= 40) then start_hand = 40
@hostinfo.start_hand = start_hand
if (param = rule.match /(^|,|,)(DRAW|DR)(\d+)(,|,|$)/)
draw_count = parseInt(param[3])
if (draw_count >= 35) then draw_count = 35
@hostinfo.draw_count = draw_count
if (param = rule.match /(^|,|,)(LFLIST|LF)(\d+)(,|,|$)/)
lflist = parseInt(param[3]) - 1
@hostinfo.lflist = lflist
if (rule.match /(^|,|,)(NOLFLIST|NF)(,|,|$)/)
@hostinfo.lflist = -1
if (rule.match /(^|,|,)(NOUNIQUE|NU)(,|,|$)/)
@hostinfo.rule = 3
if (rule.match /(^|,|,)(NOCHECK|NC)(,|,|$)/)
@hostinfo.no_check_deck = true
if (rule.match /(^|,|,)(NOSHUFFLE|NS)(,|,|$)/)
@hostinfo.no_shuffle_deck = true
if (rule.match /(^|,|,)(IGPRIORITY|PR)(,|,|$)/)
@hostinfo.enable_priority = true
param = [0, @hostinfo.lflist, @hostinfo.rule, @hostinfo.mode, (if @hostinfo.enable_priority then 'T' else 'F'),
(if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck then 'T' else 'F'),
@hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit]
try
@process = spawn './ygopro', param, {cwd: settings.ygopro_path}
@process.on 'exit', (code)=>
@disconnector = 'server' unless @disconnector
this.delete()
return
@process.stdout.setEncoding('utf8')
@process.stdout.once 'data', (data)=>
@established = true
roomlist.create(this) if !@private and settings.modules.enable_websocket_roomlist
@port = parseInt data
_.each @players, (player)=>
player.server.connect @port, '127.0.0.1', =>
player.server.write buffer for buffer in player.pre_establish_buffers
player.established = true
player.pre_establish_buffers = []
return
return
console.log @windbot
if @windbot
spawn 'mono', ['WindBot.exe'], {
cwd: 'windbot', env: {
YGOPRO_VERSION: settings.version
YGOPRO_HOST: '127.0.0.1'
YGOPRO_PORT: @port
YGOPRO_NAME: @windbot.name
YGOPRO_DECK: @windbot.deck
YGOPRO_DIALOG: @windbot.dialog
}
}
return
catch
@error = "建立房间失败,请重试"
delete: ->
#积分
return if @deleted
#log.info 'room-delete', this.name, Room.all.length
###
if @player_datas.length
replay_buffer = Buffer.concat(@watcher_buffers).toString('binary')
player_names=@player_datas[0].name + (if @player_datas[2] then "+" + @player_datas[2].name else "") +
" VS " +
@player_datas[1].name + (if @player_datas[3] then "+" + @player_datas[3].name else "")
date_time=moment().format('YYYY-MM-DD HH:mm:ss')
replay_id=Math.floor(Math.random()*100000000)
redisdb.hmset("replay:"+replay_id,
"replay_id", replay_id,
"replay_buffer", replay_buffer,
"player_names", player_names,
"date_time", date_time)
recorded_ip=[]
_.each @player_datas, (player)=>
return if _.contains(recorded_ip, player.ip)
recorded_ip.push player.ip
redisdb.lpush(player.ip+":replays", replay_id)
return
###
@watcher_buffers = []
@players = []
@watcher.end() if @watcher
@deleted = true
index = _.indexOf(Room.all, this)
#Room.all[index] = null unless index == -1
Room.all.splice(index, 1) unless index == -1
roomlist.delete @name if !@private and !@started and @established and settings.modules.enable_websocket_roomlist
return
get_playing_player: ->
playing_player = []
_.each @players, (player)=>
if player.pos < 4 then playing_player.push player
return
return playing_player
get_host: ->
host_player = null
_.each @players, (player)=>
if player.is_host then host_player = player
return
return host_player
connect: (client)->
@players.push client
client.ip = client.remoteAddress
if @random_type
host_player = @get_host()
if host_player && (host_player != client)
#进来时已经有人在等待了,互相记录为匹配过
Room.players_oppentlist[host_player.remoteAddress] = client.remoteAddress
Room.players_oppentlist[client.remoteAddress] = host_player.remoteAddress
else
#第一个玩家刚进来,还没就位
Room.players_oppentlist[client.remoteAddress] = null
if @established
roomlist.update(this) if !@private and !@started and settings.modules.enable_websocket_roomlist
client.server.connect @port, '127.0.0.1', ->
client.server.write buffer for buffer in client.pre_establish_buffers
client.established = true
client.pre_establish_buffers = []
return
return
disconnect: (client, error)->
if client.is_post_watcher
ygopro.stoc_send_chat_to_room this, "#{client.name} #{'退出了观战'}#{if error then ": #{error}" else ''}"
index = _.indexOf(@watchers, client)
@watchers.splice(index, 1) unless index == -1
#client.room = null
else
index = _.indexOf(@players, client)
@players.splice(index, 1) unless index == -1
#log.info(@started,@disconnector,client.room.random_type)
if @started and @disconnector != 'server' and client.room.random_type
Room.ban_player(client.name, client.ip, "强退")
if @players.length
ygopro.stoc_send_chat_to_room this, "#{client.name} #{'离开了游戏'}#{if error then ": #{error}" else ''}"
roomlist.update(this) if !@private and !@started and settings.modules.enable_websocket_roomlist
#client.room = null
else
@process.kill()
#client.room = null
this.delete()
return
module.exports = Room
// Generated by CoffeeScript 1.10.0
(function() {
var Room, _, bunyan, get_memory_usage, log, moment, roomlist, settings, spawn, spawnSync, ygopro;
_ = require('underscore');
_.str = require('underscore.string');
_.mixin(_.str.exports());
spawn = require('child_process').spawn;
spawnSync = require('child_process').spawnSync;
settings = require('./config.json');
ygopro = require('./ygopro.js');
if (settings.modules.enable_websocket_roomlist) {
roomlist = require('./roomlist');
}
bunyan = require('bunyan');
moment = require('moment');
moment.locale('zh-cn', {
relativeTime: {
future: '%s内',
past: '%s前',
s: '%d秒',
m: '1分钟',
mm: '%d分钟',
h: '1小时',
hh: '%d小时',
d: '1天',
dd: '%d天',
M: '1个月',
MM: '%d个月',
y: '1年',
yy: '%d年'
}
});
log = bunyan.createLogger({
name: "mycard-room"
});
get_memory_usage = function() {
var actualFree, buffers, cached, free, line, lines, percentUsed, prc_free, total;
prc_free = spawnSync("free", []);
if (prc_free.stdout) {
lines = prc_free.stdout.toString().split(/\n/g);
line = lines[1].split(/\s+/);
total = parseInt(line[1], 10);
free = parseInt(line[3], 10);
buffers = parseInt(line[5], 10);
cached = parseInt(line[6], 10);
actualFree = free + buffers + cached;
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2));
} else {
percentUsed = 0;
}
return percentUsed;
};
Room = (function() {
Room.all = [];
Room.players_oppentlist = {};
Room.players_banned = [];
Room.ban_player = function(name, ip, reason) {
var bannedplayer, bantime;
bannedplayer = _.find(Room.players_banned, function(bannedplayer) {
return ip === bannedplayer.ip;
});
if (bannedplayer) {
bannedplayer.count = bannedplayer.count + 1;
bantime = bannedplayer.count > 3 ? Math.pow(2, bannedplayer.count - 3) * 2 : 0;
bannedplayer.time = moment() < bannedplayer.time ? moment(bannedplayer.time).add(bantime, 'm') : moment().add(bantime, 'm');
if (!_.find(bannedplayer.reasons, function(bannedreason) {
return bannedreason === reason;
})) {
bannedplayer.reasons.push(reason);
}
bannedplayer.need_tip = true;
} else {
bannedplayer = {
"ip": ip,
"time": moment(),
"count": 1,
"reasons": [reason],
"need_tip": true
};
Room.players_banned.push(bannedplayer);
}
log.info("banned", name, ip, reason, bannedplayer.count);
};
Room.find_or_create_by_name = function(name, player_ip) {
var room;
if (settings.modules.enable_random_duel && (name === '' || name.toUpperCase() === 'S' || name.toUpperCase() === 'M' || name.toUpperCase() === 'T')) {
return this.find_or_create_random(name.toUpperCase(), player_ip);
}
if (room = this.find_by_name(name)) {
return room;
} else if (get_memory_usage() >= 90) {
return null;
} else {
return new Room(name);
}
};
Room.find_or_create_random = function(type, player_ip) {
var bannedplayer, max_player, name, playerbanned, result;
bannedplayer = _.find(Room.players_banned, function(bannedplayer) {
return player_ip === bannedplayer.ip;
});
if (bannedplayer) {
if (bannedplayer.count > 6 && moment() < bannedplayer.time) {
return {
"error": "因为您近期在游戏中多次" + (bannedplayer.reasons.join('')) + ",您已被禁止使用随机对战功能,将在" + (moment(bannedplayer.time).fromNow(true)) + "后解封"
};
}
if (bannedplayer.count > 3 && moment() < bannedplayer.time && bannedplayer.need_tip) {
bannedplayer.need_tip = false;
return {
"error": "因为您近期在游戏中" + (bannedplayer.reasons.join('')) + ",在" + (moment(bannedplayer.time).fromNow(true)) + "内您随机对战时只能遇到其他违规玩家"
};
} else if (bannedplayer.need_tip) {
bannedplayer.need_tip = false;
return {
"error": "系统检测到您近期在游戏中" + (bannedplayer.reasons.join('')) + ",若您违规超过3次,将受到惩罚"
};
} else if (bannedplayer.count > 2) {
bannedplayer.need_tip = true;
}
}
max_player = type === 'T' ? 4 : 2;
playerbanned = bannedplayer && bannedplayer.count > 3 && moment() < bannedplayer.time;
result = _.find(this.all, function(room) {
return room.random_type !== '' && !room.started && ((type === '' && room.random_type !== 'T') || room.random_type === type) && room.get_playing_player().length < max_player && (room.get_host() === null || room.get_host().remoteAddress !== Room.players_oppentlist[player_ip]) && (playerbanned === room.deprecated);
});
if (result) {
result.welcome = '对手已经在等你了,开始决斗吧!';
} else {
type = type ? type : 'S';
name = type + ',RANDOM#' + Math.floor(Math.random() * 100000);
result = new Room(name);
result.random_type = type;
result.max_player = max_player;
result.welcome = '已建立随机对战房间,正在等待对手!';
result.deprecated = playerbanned;
}
return result;
};
Room.find_by_name = function(name) {
var result;
result = _.find(this.all, function(room) {
return room.name === name;
});
return result;
};
Room.find_by_port = function(port) {
return _.find(this.all, function(room) {
return room.port === port;
});
};
Room.validate = function(name) {
var client_name, client_name_and_pass, client_pass;
client_name_and_pass = name.split('$', 2);
client_name = client_name_and_pass[0];
client_pass = client_name_and_pass[1];
if (!client_pass) {
return true;
}
return !_.find(Room.all, function(room) {
var room_name, room_name_and_pass, room_pass;
room_name_and_pass = room.name.split('$', 2);
room_name = room_name_and_pass[0];
room_pass = room_name_and_pass[1];
return client_name === room_name && client_pass !== room_pass;
});
};
function Room(name, hostinfo) {
var draw_count, error1, lflist, param, rule, start_hand, start_lp, time_limit;
this.hostinfo = hostinfo;
this.name = name;
this.alive = true;
this.players = [];
this.player_datas = [];
this.status = 'starting';
this.started = false;
this.established = false;
this.watcher_buffers = [];
this.watchers = [];
this.random_type = '';
this.welcome = '';
Room.all.push(this);
this.hostinfo || (this.hostinfo = {
lflist: _.findIndex(settings.lflist, function(list) {
return !list.tcg && list.date.isBefore();
}),
rule: settings.modules.enable_TCG_as_default ? 2 : 0,
mode: 0,
enable_priority: false,
no_check_deck: false,
no_shuffle_deck: false,
start_lp: 8000,
start_hand: 5,
draw_count: 1,
time_limit: 180
});
if (name.slice(0, 2) === 'M#') {
this.hostinfo.mode = 1;
} else if (name.slice(0, 2) === 'T#') {
this.hostinfo.mode = 2;
this.hostinfo.start_lp = 16000;
} else if ((param = name.match(/^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i))) {
this.hostinfo.rule = parseInt(param[1]);
this.hostinfo.mode = parseInt(param[2]);
this.hostinfo.enable_priority = param[3] === 'T';
this.hostinfo.no_check_deck = param[4] === 'T';
this.hostinfo.no_shuffle_deck = param[5] === 'T';
this.hostinfo.start_lp = parseInt(param[6]);
this.hostinfo.start_hand = parseInt(param[7]);
this.hostinfo.draw_count = parseInt(param[8]);
} else if (((param = name.match(/(.+)#/)) !== null) && ((param[1].length <= 2 && param[1].match(/(S|N|M|T)(0|1|2|T|A)/i)) || (param[1].match(/^(S|N|M|T)(0|1|2|O|T|A)(0|1|O|T)/i)))) {
rule = param[1].toUpperCase();
switch (rule.charAt(0)) {
case "M":
case "1":
this.hostinfo.mode = 1;
break;
case "T":
case "2":
this.hostinfo.mode = 2;
this.hostinfo.start_lp = 16000;
break;
default:
this.hostinfo.mode = 0;
}
switch (rule.charAt(1)) {
case "0":
case "O":
this.hostinfo.rule = 0;
break;
case "1":
case "T":
this.hostinfo.rule = 1;
break;
default:
this.hostinfo.rule = 2;
}
switch (rule.charAt(2)) {
case "1":
case "T":
this.hostinfo.lflist = _.findIndex(settings.lflist, function(list) {
return list.tcg && list.date.isBefore();
});
break;
default:
this.hostinfo.lflist = _.findIndex(settings.lflist, function(list) {
return !list.tcg && list.date.isBefore();
});
}
if ((param = parseInt(rule.charAt(3).match(/\d/))) >= 0) {
this.hostinfo.time_limit = param * 60;
}
switch (rule.charAt(4)) {
case "T":
case "1":
this.hostinfo.enable_priority = true;
break;
default:
this.hostinfo.enable_priority = false;
}
switch (rule.charAt(5)) {
case "T":
case "1":
this.hostinfo.no_check_deck = true;
break;
default:
this.hostinfo.no_check_deck = false;
}
switch (rule.charAt(6)) {
case "T":
case "1":
this.hostinfo.no_shuffle_deck = true;
break;
default:
this.hostinfo.no_shuffle_deck = false;
}
if ((param = parseInt(rule.charAt(7).match(/\d/))) > 0) {
this.hostinfo.start_lp = param * 4000;
}
if ((param = parseInt(rule.charAt(8).match(/\d/))) > 0) {
this.hostinfo.start_hand = param;
}
if ((param = parseInt(rule.charAt(9).match(/\d/))) >= 0) {
this.hostinfo.draw_count = param;
}
} else if ((param = name.match(/(.+)#/)) !== null) {
rule = param[1].toUpperCase();
if (rule.match(/(^|,|,)(M|MATCH)(,|,|$)/)) {
this.hostinfo.mode = 1;
}
if (rule.match(/(^|,|,)(T|TAG)(,|,|$)/)) {
this.hostinfo.mode = 2;
this.hostinfo.start_lp = 16000;
}
if (rule.match(/(^|,|,)(TCGONLY|TO)(,|,|$)/)) {
this.hostinfo.rule = 1;
this.hostinfo.lflist = _.findIndex(settings.lflist, function(list) {
return list.tcg && list.date.isBefore();
});
}
if (rule.match(/(^|,|,)(OCGONLY|OO)(,|,|$)/)) {
this.hostinfo.rule = 0;
}
if (rule.match(/(^|,|,)(OT|TCG)(,|,|$)/)) {
this.hostinfo.rule = 2;
}
if ((param = rule.match(/(^|,|,)LP(\d+)(,|,|$)/))) {
start_lp = parseInt(param[2]);
if (start_lp <= 0) {
start_lp = 1;
}
if (start_lp >= 99999) {
start_lp = 99999;
}
this.hostinfo.start_lp = start_lp;
}
if ((param = rule.match(/(^|,|,)(TIME|TM|TI)(\d+)(,|,|$)/))) {
time_limit = parseInt(param[3]);
if (time_limit < 0) {
time_limit = 180;
}
if (time_limit >= 1 && time_limit <= 60) {
time_limit = time_limit * 60;
}
if (time_limit >= 999) {
time_limit = 999;
}
this.hostinfo.time_limit = time_limit;
}
if ((param = rule.match(/(^|,|,)(START|ST)(\d+)(,|,|$)/))) {
start_hand = parseInt(param[3]);
if (start_hand <= 0) {
start_hand = 1;
}
if (start_hand >= 40) {
start_hand = 40;
}
this.hostinfo.start_hand = start_hand;
}
if ((param = rule.match(/(^|,|,)(DRAW|DR)(\d+)(,|,|$)/))) {
draw_count = parseInt(param[3]);
if (draw_count >= 35) {
draw_count = 35;
}
this.hostinfo.draw_count = draw_count;
}
if ((param = rule.match(/(^|,|,)(LFLIST|LF)(\d+)(,|,|$)/))) {
lflist = parseInt(param[3]) - 1;
this.hostinfo.lflist = lflist;
}
if (rule.match(/(^|,|,)(NOLFLIST|NF)(,|,|$)/)) {
this.hostinfo.lflist = -1;
}
if (rule.match(/(^|,|,)(NOUNIQUE|NU)(,|,|$)/)) {
this.hostinfo.rule = 3;
}
if (rule.match(/(^|,|,)(NOCHECK|NC)(,|,|$)/)) {
this.hostinfo.no_check_deck = true;
}
if (rule.match(/(^|,|,)(NOSHUFFLE|NS)(,|,|$)/)) {
this.hostinfo.no_shuffle_deck = true;
}
if (rule.match(/(^|,|,)(IGPRIORITY|PR)(,|,|$)/)) {
this.hostinfo.enable_priority = true;
}
}
param = [0, this.hostinfo.lflist, this.hostinfo.rule, this.hostinfo.mode, (this.hostinfo.enable_priority ? 'T' : 'F'), (this.hostinfo.no_check_deck ? 'T' : 'F'), (this.hostinfo.no_shuffle_deck ? 'T' : 'F'), this.hostinfo.start_lp, this.hostinfo.start_hand, this.hostinfo.draw_count, this.hostinfo.time_limit];
try {
this.process = spawn('./ygopro', param, {
cwd: settings.ygopro_path
});
this.process.on('exit', (function(_this) {
return function(code) {
if (!_this.disconnector) {
_this.disconnector = 'server';
}
_this["delete"]();
};
})(this));
this.process.stdout.setEncoding('utf8');
this.process.stdout.once('data', (function(_this) {
return function(data) {
_this.established = true;
if (!_this["private"] && settings.modules.enable_websocket_roomlist) {
roomlist.create(_this);
}
_this.port = parseInt(data);
_.each(_this.players, function(player) {
player.server.connect(_this.port, '127.0.0.1', function() {
var buffer, i, len, ref;
ref = player.pre_establish_buffers;
for (i = 0, len = ref.length; i < len; i++) {
buffer = ref[i];
player.server.write(buffer);
}
player.established = true;
player.pre_establish_buffers = [];
});
});
console.log(_this.windbot);
if (_this.windbot) {
spawn('mono', ['WindBot.exe'], {
cwd: 'windbot',
env: {
YGOPRO_VERSION: settings.version,
YGOPRO_HOST: '127.0.0.1',
YGOPRO_PORT: _this.port,
YGOPRO_NAME: _this.windbot.name,
YGOPRO_DECK: _this.windbot.deck,
YGOPRO_DIALOG: _this.windbot.dialog
}
});
}
};
})(this));
} catch (error1) {
this.error = "建立房间失败,请重试";
}
}
Room.prototype["delete"] = function() {
var index;
if (this.deleted) {
return;
}
/*
if @player_datas.length
replay_buffer = Buffer.concat(@watcher_buffers).toString('binary')
player_names=@player_datas[0].name + (if @player_datas[2] then "+" + @player_datas[2].name else "") +
" VS " +
@player_datas[1].name + (if @player_datas[3] then "+" + @player_datas[3].name else "")
date_time=moment().format('YYYY-MM-DD HH:mm:ss')
replay_id=Math.floor(Math.random()*100000000)
redisdb.hmset("replay:"+replay_id,
"replay_id", replay_id,
"replay_buffer", replay_buffer,
"player_names", player_names,
"date_time", date_time)
recorded_ip=[]
_.each @player_datas, (player)=>
return if _.contains(recorded_ip, player.ip)
recorded_ip.push player.ip
redisdb.lpush(player.ip+":replays", replay_id)
return
*/
this.watcher_buffers = [];
this.players = [];
if (this.watcher) {
this.watcher.end();
}
this.deleted = true;
index = _.indexOf(Room.all, this);
if (index !== -1) {
Room.all.splice(index, 1);
}
if (!this["private"] && !this.started && this.established && settings.modules.enable_websocket_roomlist) {
roomlist["delete"](this.name);
}
};
Room.prototype.get_playing_player = function() {
var playing_player;
playing_player = [];
_.each(this.players, (function(_this) {
return function(player) {
if (player.pos < 4) {
playing_player.push(player);
}
};
})(this));
return playing_player;
};
Room.prototype.get_host = function() {
var host_player;
host_player = null;
_.each(this.players, (function(_this) {
return function(player) {
if (player.is_host) {
host_player = player;
}
};
})(this));
return host_player;
};
Room.prototype.connect = function(client) {
var host_player;
this.players.push(client);
client.ip = client.remoteAddress;
if (this.random_type) {
host_player = this.get_host();
if (host_player && (host_player !== client)) {
Room.players_oppentlist[host_player.remoteAddress] = client.remoteAddress;
Room.players_oppentlist[client.remoteAddress] = host_player.remoteAddress;
} else {
Room.players_oppentlist[client.remoteAddress] = null;
}
}
if (this.established) {
if (!this["private"] && !this.started && settings.modules.enable_websocket_roomlist) {
roomlist.update(this);
}
client.server.connect(this.port, '127.0.0.1', function() {
var buffer, i, len, ref;
ref = client.pre_establish_buffers;
for (i = 0, len = ref.length; i < len; i++) {
buffer = ref[i];
client.server.write(buffer);
}
client.established = true;
client.pre_establish_buffers = [];
});
}
};
Room.prototype.disconnect = function(client, error) {
var index;
if (client.is_post_watcher) {
ygopro.stoc_send_chat_to_room(this, client.name + " " + '退出了观战' + (error ? ": " + error : ''));
index = _.indexOf(this.watchers, client);
if (index !== -1) {
this.watchers.splice(index, 1);
}
} else {
index = _.indexOf(this.players, client);
if (index !== -1) {
this.players.splice(index, 1);
}
if (this.started && this.disconnector !== 'server' && client.room.random_type) {
Room.ban_player(client.name, client.ip, "强退");
}
if (this.players.length) {
ygopro.stoc_send_chat_to_room(this, client.name + " " + '离开了游戏' + (error ? ": " + error : ''));
if (!this["private"] && !this.started && settings.modules.enable_websocket_roomlist) {
roomlist.update(this);
}
} else {
this.process.kill();
this["delete"]();
}
}
};
return Room;
})();
module.exports = Room;
}).call(this);
WebSocketServer = require('ws').Server;
WebSocketServer = require('ws').Server
server = null
......
// a special version of node-struct by xdenser
// https://github.com/xdenser/node-struct/tree/f843487d6768cd0bf20c2ce7803dde2d92df5694
function byteField(p, offset) {
this.length = 1;
this.get = function () {
......
......@@ -92,7 +92,7 @@
"deck": [
{"name": "mainc", "type": "unsigned int"},
{"name": "sidec", "type": "unsigned int"},
{"name": "deckbuf", "type": "unsigned int", "length": 75}
{"name": "deckbuf", "type": "unsigned int", "length": 90}
],
"chat": [
{"name": "msg", "type": "unsigned short", "length":"255", "encoding": "UTF-16LE"}
......
......@@ -8,11 +8,9 @@
*/
var sqlite3 = require('sqlite3').verbose();
var fs = require('fs');
var config = require('./config.deckstats.json'); //{ "deckpath": "../decks", "dbfile": "cards.cdb" }
var constants = require('./constants.json');
var DECKPATH="C:\\deck\\deck"
var DBFILE="F:\\Works\\deck\\cards.cdb"
var ALL_MAIN_CARDS={};
var ALL_SIDE_CARDS={};
var ALL_CARD_DATAS={};
......@@ -27,6 +25,9 @@ function add_to_deck(deck,id) {
}
function add_to_all_list(LIST,id,use) {
if (!ALL_CARD_DATAS[id]) {
return;
}
if (ALL_CARD_DATAS[id].alias) {
id=ALL_CARD_DATAS[id].alias;
}
......@@ -46,7 +47,7 @@ function add_to_all_list(LIST,id,use) {
function read_deck_file(filename) {
console.log("reading "+filename);
var deck_text=fs.readFileSync(DECKPATH+"\\"+filename,{encoding:"ASCII"})
var deck_text=fs.readFileSync(config.deckpath+"\\"+filename,{encoding:"ASCII"})
var deck_array=deck_text.split("\n");
var deck_main={};
var deck_side={};
......@@ -68,10 +69,10 @@ function read_deck_file(filename) {
}
function load_database(callback) {
var db=new sqlite3.Database(DBFILE);
var db=new sqlite3.Database(config.dbfile);
db.each("select * from datas,texts where datas.id=texts.id", function (err,result) {
if (err) {
console.log(DBFILE + ":" + err);
console.log(config.dbfile + ":" + err);
return;
}
else {
......@@ -141,7 +142,7 @@ function load_database(callback) {
}
function read_decks() {
var ALL_DECKS=fs.readdirSync(DECKPATH);
var ALL_DECKS=fs.readdirSync(config.deckpath);
for (var i in ALL_DECKS) {
var filename=ALL_DECKS[i];
......@@ -150,7 +151,12 @@ function read_decks() {
}
}
output_csv(ALL_MAIN_CARDS,"main.csv");
if (ALL_SIDE_CARDS.length) {
var ALL_SIDE_CARDS_isempty = true;
for (var j in ALL_SIDE_CARDS) {
ALL_SIDE_CARDS_isempty = false;
break;
}
if (!ALL_SIDE_CARDS_isempty) {
output_csv(ALL_SIDE_CARDS,"side.csv");
}
}
......
......@@ -17,7 +17,13 @@ var moment = require('moment');
moment.locale('zh-cn');
var constants = require('./constants.json');
var config = require('./config.pre.json');
var nconf = require('nconf');
nconf.file('./config.user.json');
var defaultconfig = require('./config.json');
nconf.defaults(defaultconfig);
var settings = nconf.get();
config=settings.modules.pre_util;
//全卡HTML列表
var cardHTMLs=[];
......@@ -52,7 +58,7 @@ var loadDb = function(db_file) {
var cardHTML="<tr>";
cardHTML+='<td><a href="'+ config.html_img_rel_path + result.id +'.jpg" target="_blank"><img src="'+config.html_img_rel_path+'thumbnail/'+ result.id +'.jpg" alt="'+ result.name +'"></a></td>';
cardHTML+='<td><a href="'+ config.html_img_rel_path + result.id +'.jpg" target="_blank"><img src="'+config.html_img_rel_path+config.html_img_thumbnail+ result.id +'.jpg" alt="'+ result.name +'"></a></td>';
cardHTML+='<td>'+ result.name +'</td>';
var cardText="";
......@@ -92,7 +98,7 @@ var loadDb = function(db_file) {
if (result.race & constants.RACES.RACE_ZOMBIE) {cardRace="不死";}
if (result.race & constants.RACES.RACE_MACHINE) {cardRace="机械";}
if (result.race & constants.RACES.RACE_AQUA) {cardRace="";}
if (result.race & constants.RACES.RACE_PYRO) {cardRace="念动力";}
if (result.race & constants.RACES.RACE_PYRO) {cardRace="";}
if (result.race & constants.RACES.RACE_ROCK) {cardRace="岩石";}
if (result.race & constants.RACES.RACE_WINDBEAST) {cardRace="鸟兽";}
if (result.race & constants.RACES.RACE_PLANT) {cardRace="植物";}
......@@ -119,7 +125,7 @@ var loadDb = function(db_file) {
if (result.attribute & constants.ATTRIBUTES.ATTRIBUTE_LIGHT) {cardAttr="";}
if (result.attribute & constants.ATTRIBUTES.ATTRIBUTE_DARK) {cardAttr="";}
if (result.attribute & constants.ATTRIBUTES.ATTRIBUTE_DEVINE) {cardAttr="";}
cardText+="/"+ cardAttr +"\r\n";
cardText+="/"+ cardAttr +"\n";
var cardLevel;
var cardLScale;
......@@ -141,14 +147,14 @@ var loadDb = function(db_file) {
cardText+=" " + cardLScale + "/" +cardRScale;
}
cardText+="\r\n";
cardText+="\n";
}
else {
cardText+="\r\n";
cardText+="\n";
}
cardText+=result.desc;
cardHTML+='<td>'+ cardText.replace(/\r\n/g,"<br>") +'</td>';
cardHTML+='<td>'+ cardText.replace(/\n/g,"<br>") +'</td>';
cardHTML+='</tr>';
cardHTMLs.push(cardHTML);
......@@ -166,11 +172,11 @@ var loadDb = function(db_file) {
//将cardHTMLs中内容更新到指定列表页,同步
var writeToFile = function(message) {
var fileContent=fs.readFileSync(config.html_path+config.html_filename, {"encoding":"utf-8"});
var newContent=cardHTMLs.join("\r\n");
fileContent=fileContent.replace(/<tbody class="auto-generated">[\w\W]*<\/tbody>/,'<tbody class="auto-generated">\r\n'+newContent+'\r\n</tbody>');
var newContent=cardHTMLs.join("\n");
fileContent=fileContent.replace(/<tbody class="auto-generated">[\w\W]*<\/tbody>/,'<tbody class="auto-generated">\n'+newContent+'\n</tbody>');
if (message) {
message="<li>"+moment().format('L HH:mm')+"<ul><li>"+message.split("!换行符!").join("</li><li>")+"</li></ul></li>";
fileContent=fileContent.replace(/<ul class="auto-generated">/,'<ul class="auto-generated">\r\n'+message);
fileContent=fileContent.replace(/<ul class="auto-generated">/,'<ul class="auto-generated">\n'+message);
}
fs.writeFileSync(config.html_path+config.html_filename, fileContent);
sendResponse("列表更新完成。");
......@@ -183,7 +189,7 @@ var loadAllDbs = function() {
var files = fs.readdirSync(config.db_path+"expansions/");
for (var i in files) {
var filename = files[i];
if (filename.slice(-4) === ".cdb") {
if (filename.slice(-4) === ".cdb" && (!config.only_show_dbs || config.only_show_dbs.length==0 || config.only_show_dbs[filename])) {
loadDb(config.db_path+"expansions/"+filename);
}
}
......@@ -198,7 +204,7 @@ var fetchDatas = function() {
});
proc.stderr.setEncoding('utf8');
proc.stderr.on('data', function(data) {
sendResponse("git pull error: "+data);
sendResponse("git pull: "+data);
});
proc.on('close', function (code) {
sendResponse("数据更新完成。");
......@@ -210,7 +216,7 @@ var fetchDatas = function() {
});
proc2.stderr.setEncoding('utf8');
proc2.stderr.on('data', function(data) {
sendResponse("git pull error: "+data);
sendResponse("git pull: "+data);
});
proc2.on('close', function (code) {
sendResponse("网页同步完成。");
......@@ -225,30 +231,27 @@ var pushDatas = function() {
} catch (error) {
sendResponse("git error: "+error.stdout);
}
var proc2 = spawn("git", ["push", "gitcafe", "master:gitcafe-pages"], { cwd: config.git_html_path, env: process.env });
proc2.stdout.setEncoding('utf8');
proc2.stdout.on('data', function(data) {
sendResponse("git push: "+data);
});
proc2.stderr.setEncoding('utf8');
proc2.stderr.on('data', function(data) {
sendResponse("git push error: "+data);
});
proc2.on('close', function (code) {
sendResponse("gitcafe上传完成。");
});
var proc = spawn("git", ["push"], { cwd: config.git_html_path, env: process.env });
for (var i in config.html_gits) {
var git = config.html_gits[i];
var proc = spawn("git", git.push, { cwd: config.git_html_path, env: process.env });
proc.stdout.setEncoding('utf8');
proc.stdout.on('data', function(data) {
sendResponse("git push: "+data);
});
proc.stdout.on('data', (function(git) {
return function(data) {
sendResponse(git.name + " git push: " + data);
}
})(git));
proc.stderr.setEncoding('utf8');
proc.stderr.on('data', function(data) {
sendResponse("git push: "+data);
});
proc.on('close', function (code) {
sendResponse("github上传完成。");
});
proc.stderr.on('data', (function(git) {
return function(data) {
sendResponse(git.name + " git push: " + data);
}
})(git));
proc.on('close', (function(git) {
return function(code) {
sendResponse(git.name + "上传完成。");
}
})(git));
}
}
//将数据库文件夹里卡图复制到列表页对应文件夹里,同步
......@@ -260,9 +263,13 @@ var copyImages = function() {
//将数据库文件夹复制到YGOPRO文件夹里,同步
var copyToYGOPRO = function() {
execSync('rm -rf "' + config.ygopro_path + 'expansions/*' + '"');
execSync('rm -rf ' + config.ygopro_path + 'expansions/*' + '');
execSync('cp -rf "' + config.db_path + 'expansions' + '" "' + config.ygopro_path + '"');
execSync('cp -rf "' + config.db_path + 'script' + '" "' + config.ygopro_path + 'expansions"');
try {
execSync('cp -rf "' + config.db_path + 'lflist.conf' + '" "' + config.ygopro_path + '"');
}
catch (e) {}
sendResponse("更新完成。");
}
......
#标准库
# 标准库
net = require 'net'
http = require 'http'
url = require 'url'
......@@ -7,52 +7,125 @@ fs = require 'fs'
os = require 'os'
crypto = require 'crypto'
execFile = require('child_process').execFile
spawn = require('child_process').spawn
spawnSync = require('child_process').spawnSync
#三方库
# 三方库
_ = require 'underscore'
_.str = require 'underscore.string'
_.mixin(_.str.exports());
_.mixin(_.str.exports())
request = require 'request'
bunyan = require 'bunyan'
log = bunyan.createLogger name: "mycard"
moment = require 'moment'
#redis = require 'redis'
#redisdb = redis.createClient host: "127.0.0.1", port: settings.modules.redis_port
moment.locale('zh-cn', {
relativeTime: {
future: '%s内',
past: '%s前',
s: '%d秒',
m: '1分钟',
mm: '%d分钟',
h: '1小时',
hh: '%d小时',
d: '1天',
dd: '%d天',
M: '1个月',
MM: '%d个月',
y: '1年',
yy: '%d年'
}
})
#heapdump = require 'heapdump'
#配置文件
settings = require './config.json'
settings.BANNED_user = []
settings.BANNED_IP = []
settings.modules.hang_timeout = 90
# 配置
# use nconf to save user config.user.json .
# config.json shouldn't be changed
nconf = require 'nconf'
nconf.file('./config.user.json')
defaultconfig = require('./config.json')
nconf.defaults(defaultconfig)
settings = global.settings = nconf.get()
nconf.myset = (settings, path, val) ->
# path should be like "modules:welcome"
nconf.set(path, val)
nconf.save()
log.info("setting changed", path, val) if _.isString(val)
path=path.split(':')
if path.length == 0
settings[path[0]]=val
else
target=settings
while path.length > 1
key=path.shift()
target=target[key]
key = path.shift()
target[key] = val
return
# ban a user manually and permanently
ban_user = (name) ->
settings.ban.banned_user.push(name)
nconf.myset(settings, "ban:banned_user", settings.ban.banned_user)
bad_ip=0
for room in ROOM_all when room and room.established
for player in room.players
if player and (player.name == name or player.ip == bad_ip)
bad_ip = player.ip
ROOM_bad_ip.push(player.ip)
settings.ban.banned_ip.push(player.ip)
ygopro.stoc_send_chat_to_room(room, "#{player.name} 被系统请出了房间", ygopro.constants.COLORS.RED)
player.destroy()
continue
return
settings.version = parseInt(fs.readFileSync('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
# load the lflist of current date
settings.lflist = (for list in fs.readFileSync('ygopro/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
{date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
if settings.modules.enable_cloud_replay
redis = require 'redis'
zlib = require 'zlib'
redisdb = redis.createClient host: "127.0.0.1", port: settings.modules.redis_port
redisdb.on 'error', (err)->
log.warn err
return
if settings.modules.enable_windbot
settings.modules.windbot = require('./windbot/bots.json').windbots
settings.modules.windbots = require('./windbot/bots.json').windbots
#组件
# 组件
ygopro = require './ygopro.js'
Room = require './room.js'
roomlist = require './roomlist.js' if settings.modules.enable_websocket_roomlist
# cache users of mycard login
users_cache = {}
#debug模式 端口号+1
debug = false
log = null
if process.argv[2] == '--debug'
settings.port++
settings.modules.http.port++ if settings.modules.http
log = bunyan.createLogger name: "mycard-debug"
else
log = bunyan.createLogger name: "mycard"
#定时清理关闭的连接
# 获取可用内存
get_memory_usage = ()->
prc_free = spawnSync("free", [])
if (prc_free.stdout)
lines = prc_free.stdout.toString().split(/\n/g)
line = lines[1].split(/\s+/)
total = parseInt(line[1], 10)
free = parseInt(line[3], 10)
buffers = parseInt(line[5], 10)
cached = parseInt(line[6], 10)
actualFree = free + buffers + cached
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2))
else
percentUsed = 0
return percentUsed
# 定时清理关闭的连接
# the server write data directly to the socket object
# so this is a dumb way to clean data
Graveyard = []
tribute = (socket) ->
......@@ -69,84 +142,513 @@ setInterval ()->
return
, 3000
#网络连接
Cloud_replay_ids = []
ROOM_all = []
ROOM_players_oppentlist = {}
ROOM_players_banned = []
ROOM_connected_ip = {}
ROOM_bad_ip = {}
# automatically ban user to use random duel
ROOM_ban_player = (name, ip, reason, countadd = 1)->
bannedplayer = _.find ROOM_players_banned, (bannedplayer)->
ip == bannedplayer.ip
if bannedplayer
bannedplayer.count = bannedplayer.count + countadd
bantime = if bannedplayer.count > 3 then Math.pow(2, bannedplayer.count - 3) * 2 else 0
bannedplayer.time = if moment() < bannedplayer.time then moment(bannedplayer.time).add(bantime, 'm') else moment().add(bantime, 'm')
bannedplayer.reasons.push(reason) if not _.find bannedplayer.reasons, (bannedreason)->
bannedreason == reason
bannedplayer.need_tip = true
else
bannedplayer = {"ip": ip, "time": moment(), "count": countadd, "reasons": [reason], "need_tip": true}
ROOM_players_banned.push(bannedplayer)
#log.info("banned", name, ip, reason, bannedplayer.count)
return
ROOM_find_or_create_by_name = (name, player_ip)->
uname=name.toUpperCase()
if settings.modules.enable_windbot and (uname[0...2] == 'AI' or (!settings.modules.enable_random_duel and uname == ''))
return ROOM_find_or_create_ai(name)
if settings.modules.enable_random_duel and (uname == '' or uname == 'S' or uname == 'M' or uname == 'T')
return ROOM_find_or_create_random(uname, player_ip)
if room = ROOM_find_by_name(name)
return room
else if get_memory_usage() >= 90
return null
else
return new Room(name)
ROOM_find_or_create_random = (type, player_ip)->
bannedplayer = _.find ROOM_players_banned, (bannedplayer)->
return player_ip == bannedplayer.ip
if bannedplayer
if bannedplayer.count > 6 and moment() < bannedplayer.time
return {"error": "因为您近期在游戏中多次#{bannedplayer.reasons.join('、')},您已被禁止使用随机对战功能,将在#{moment(bannedplayer.time).fromNow(true)}后解封"}
if bannedplayer.count > 3 and moment() < bannedplayer.time and bannedplayer.need_tip
bannedplayer.need_tip = false
return {"error": "因为您近期在游戏中#{bannedplayer.reasons.join('、')},在#{moment(bannedplayer.time).fromNow(true)}内您随机对战时只能遇到其他违规玩家"}
else if bannedplayer.need_tip
bannedplayer.need_tip = false
return {"error": "系统检测到您近期在游戏中#{bannedplayer.reasons.join('、')},若您违规超过3次,将受到惩罚"}
else if bannedplayer.count > 2
bannedplayer.need_tip = true
max_player = if type == 'T' then 4 else 2
playerbanned = (bannedplayer and bannedplayer.count > 3 and moment() < bannedplayer.time)
result = _.find ROOM_all, (room)->
return room and room.random_type != '' and !room.started and
((type == '' and room.random_type != 'T') or room.random_type == type) and
room.get_playing_player().length < max_player and
(room.get_host() == null or room.get_host().ip != ROOM_players_oppentlist[player_ip]) and
(playerbanned == room.deprecated)
if result
result.welcome = '对手已经在等你了,开始决斗吧!'
#log.info 'found room', player_name
else if get_memory_usage() < 90
type = if type then type else 'S'
name = type + ',RANDOM#' + Math.floor(Math.random() * 100000)
result = new Room(name)
result.random_type = type
result.max_player = max_player
result.welcome = '已建立随机对战房间,正在等待对手!'
result.deprecated = playerbanned
#log.info 'create room', player_name, name
else
return null
if result.random_type=='M' then result.welcome = result.welcome + '\n您进入了比赛模式的房间,我们推荐使用竞技卡组!'
return result
ROOM_find_or_create_ai = (name)->
if name == ''
name = 'AI'
if name[0...3] == 'AI_'
name = 'AI#' + name.slice(3)
namea = name.split('#')
if room = ROOM_find_by_name(name)
return room
else if name == 'AI'
windbot = _.sample settings.modules.windbots
name = 'AI#' + Math.floor(Math.random() * 100000)
else if namea.length>1
ainame = namea[namea.length-1]
windbot = _.sample _.filter settings.modules.windbots, (w)->
w.name == ainame or w.deck == ainame
if !windbot
return { "error": "未找到该AI角色或卡组" }
name = name + ',' + Math.floor(Math.random() * 100000)
else
windbot = _.sample settings.modules.windbots
name = name + '#' + Math.floor(Math.random() * 100000)
if name.replace(/[^\x00-\xff]/g,"00").length>20
log.info "long ai name", name
return { "error": "AI房间名过长,请在建立房间后输入 /ai 来添加AI" }
result = new Room(name)
result.windbot = windbot
return result
ROOM_find_by_name = (name)->
result = _.find ROOM_all, (room)->
return room and room.name == name
#log.info 'find_by_name', name, result
return result
ROOM_find_by_port = (port)->
_.find ROOM_all, (room)->
return room and room.port == port
ROOM_validate = (name)->
client_name_and_pass = name.split('$', 2)
client_name = client_name_and_pass[0]
client_pass = client_name_and_pass[1]
return true if !client_pass
!_.find ROOM_all, (room)->
return false unless room
room_name_and_pass = room.name.split('$', 2)
room_name = room_name_and_pass[0]
room_pass = room_name_and_pass[1]
client_name == room_name and client_pass != room_pass
class Room
constructor: (name, @hostinfo) ->
@name = name
@alive = true
@players = []
@player_datas = []
@status = 'starting'
@started = false
@established = false
@watcher_buffers = []
@recorder_buffers = []
@cloud_replay_id = Math.floor(Math.random()*100000000)
@watchers = []
@random_type = ''
@welcome = ''
ROOM_all.push this
@hostinfo ||=
lflist: if settings.lflist.length then 0 else -1
rule: if settings.modules.enable_TCG_as_default then 2 else 0
mode: 0
enable_priority: false
no_check_deck: false
no_shuffle_deck: false
start_lp: 8000
start_hand: 5
draw_count: 1
time_limit: 180
replay_mode: if settings.modules.tournament_mode.enabled then 1 else 0
if name[0...2] == 'M#'
@hostinfo.mode = 1
else if name[0...2] == 'T#'
@hostinfo.mode = 2
@hostinfo.start_lp = 16000
else if (param = name.match /^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i)
@hostinfo.rule = parseInt(param[1])
@hostinfo.mode = parseInt(param[2])
@hostinfo.enable_priority = param[3] == 'T'
@hostinfo.no_check_deck = param[4] == 'T'
@hostinfo.no_shuffle_deck = param[5] == 'T'
@hostinfo.start_lp = parseInt(param[6])
@hostinfo.start_hand = parseInt(param[7])
@hostinfo.draw_count = parseInt(param[8])
else if ((param = name.match /(.+)#/) != null)
rule = param[1].toUpperCase()
if (rule.match /(^|,|,)(M|MATCH)(,|,|$)/)
@hostinfo.mode = 1
if (rule.match /(^|,|,)(T|TAG)(,|,|$)/)
@hostinfo.mode = 2
@hostinfo.start_lp = 16000
if (rule.match /(^|,|,)(TCGONLY|TO)(,|,|$)/)
@hostinfo.rule = 1
@hostinfo.lflist = _.findIndex settings.lflist, (list)-> list.tcg
if (rule.match /(^|,|,)(OCGONLY|OO)(,|,|$)/)
@hostinfo.rule = 0
if (rule.match /(^|,|,)(OT|TCG)(,|,|$)/)
@hostinfo.rule = 2
if (param = rule.match /(^|,|,)LP(\d+)(,|,|$)/)
start_lp = parseInt(param[2])
if (start_lp <= 0) then start_lp = 1
if (start_lp >= 99999) then start_lp = 99999
@hostinfo.start_lp = start_lp
if (param = rule.match /(^|,|,)(TIME|TM|TI)(\d+)(,|,|$)/)
time_limit = parseInt(param[3])
if (time_limit < 0) then time_limit = 180
if (time_limit >= 1 and time_limit <= 60) then time_limit = time_limit * 60
if (time_limit >= 999) then time_limit = 999
@hostinfo.time_limit = time_limit
if (param = rule.match /(^|,|,)(START|ST)(\d+)(,|,|$)/)
start_hand = parseInt(param[3])
if (start_hand <= 0) then start_hand = 1
if (start_hand >= 40) then start_hand = 40
@hostinfo.start_hand = start_hand
if (param = rule.match /(^|,|,)(DRAW|DR)(\d+)(,|,|$)/)
draw_count = parseInt(param[3])
if (draw_count >= 35) then draw_count = 35
@hostinfo.draw_count = draw_count
if (param = rule.match /(^|,|,)(LFLIST|LF)(\d+)(,|,|$)/)
lflist = parseInt(param[3]) - 1
@hostinfo.lflist = lflist
if (rule.match /(^|,|,)(NOLFLIST|NF)(,|,|$)/)
@hostinfo.lflist = -1
if (rule.match /(^|,|,)(NOUNIQUE|NU)(,|,|$)/)
@hostinfo.rule = 3
if (rule.match /(^|,|,)(NOCHECK|NC)(,|,|$)/)
@hostinfo.no_check_deck = true
if (rule.match /(^|,|,)(NOSHUFFLE|NS)(,|,|$)/)
@hostinfo.no_shuffle_deck = true
if (rule.match /(^|,|,)(IGPRIORITY|PR)(,|,|$)/)
@hostinfo.enable_priority = true
param = [0, @hostinfo.lflist, @hostinfo.rule, @hostinfo.mode, (if @hostinfo.enable_priority then 'T' else 'F'),
(if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck then 'T' else 'F'),
@hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode]
try
@process = spawn './ygopro', param, {cwd: 'ygopro'}
@process.on 'exit', (code)=>
@disconnector = 'server' unless @disconnector
this.delete()
return
@process.stdout.setEncoding('utf8')
@process.stdout.once 'data', (data)=>
@established = true
roomlist.create(this) if !@private and settings.modules.enable_websocket_roomlist
@port = parseInt data
_.each @players, (player)=>
player.server.connect @port, '127.0.0.1', ->
player.server.write buffer for buffer in player.pre_establish_buffers
player.established = true
player.pre_establish_buffers = []
return
return
if @windbot
setTimeout ()=>
@add_windbot(@windbot)
, 200
return
@process.stderr.on 'data', (data)=>
data = "Debug: " + data
data = data.replace(/\n$/, "")
log.info "YGOPRO " + data
ygopro.stoc_send_chat_to_room this, data, ygopro.constants.COLORS.RED
@has_ygopro_error = true
return
catch
@error = "建立房间失败,请重试"
delete: ->
return if @deleted
#log.info 'room-delete', this.name, ROOM_all.length
if @player_datas.length and settings.modules.enable_cloud_replay
replay_id = @cloud_replay_id
if @has_ygopro_error
log_rep_id = true
player_names=@player_datas[0].name + (if @player_datas[2] then "+" + @player_datas[2].name else "") +
" VS " +
(if @player_datas[1] then @player_datas[1].name else "AI") +
(if @player_datas[3] then "+" + @player_datas[3].name else "")
player_ips=[]
_.each @player_datas, (player)->
player_ips.push(player.ip)
return
recorder_buffer=Buffer.concat(@recorder_buffers)
zlib.deflate recorder_buffer, (err, replay_buffer) ->
replay_buffer=replay_buffer.toString('binary')
#log.info err, replay_buffer
date_time=moment().format('YYYY-MM-DD HH:mm:ss')
#replay_id=Math.floor(Math.random()*100000000)
redisdb.hmset("replay:"+replay_id,
"replay_id", replay_id,
"replay_buffer", replay_buffer,
"player_names", player_names,
"date_time", date_time)
if !log_rep_id
redisdb.expire("replay:"+replay_id, 60*60*24)
recorded_ip=[]
_.each player_ips, (player_ip)->
return if _.contains(recorded_ip, player_ip)
recorded_ip.push player_ip
redisdb.lpush(player_ip+":replays", replay_id)
return
if log_rep_id
log.info "error replay: R#" + replay_id
return
@watcher_buffers = []
@recorder_buffers = []
@players = []
@watcher.destroy() if @watcher
@recorder.destroy() if @recorder
@deleted = true
index = _.indexOf(ROOM_all, this)
ROOM_all[index] = null unless index == -1
#ROOM_all.splice(index, 1) unless index == -1
roomlist.delete @name if !@private and !@started and @established and settings.modules.enable_websocket_roomlist
return
get_playing_player: ->
playing_player = []
_.each @players, (player)->
if player.pos < 4 then playing_player.push player
return
return playing_player
get_host: ->
host_player = null
_.each @players, (player)->
if player.is_host then host_player = player
return
return host_player
add_windbot: (botdata)->
@windbot = botdata
request
url: "http://127.0.0.1:#{settings.modules.windbot_port}/?name=#{encodeURIComponent(botdata.name)}&deck=#{encodeURIComponent(botdata.deck)}&host=127.0.0.1&port=#{settings.port}&dialog=#{encodeURIComponent(botdata.dialog)}&version=#{settings.version}&password=#{encodeURIComponent(@name)}"
, (error, response, body)=>
if error
log.warn 'windbot add error', error, this.name, response
ygopro.stoc_send_chat_to_room(this, "添加AI失败,可尝试输入 /ai 重新添加", ygopro.constants.COLORS.RED)
#else
#log.info "windbot added"
return
return
connect: (client)->
@players.push client
if @random_type
client.abuse_count = 0
host_player = @get_host()
if host_player && (host_player != client)
# 进来时已经有人在等待了,互相记录为匹配过
ROOM_players_oppentlist[host_player.ip] = client.ip
ROOM_players_oppentlist[client.ip] = host_player.ip
else
# 第一个玩家刚进来,还没就位
ROOM_players_oppentlist[client.ip] = null
if @established
roomlist.update(this) if !@private and !@started and settings.modules.enable_websocket_roomlist
client.server.connect @port, '127.0.0.1', ->
client.server.write buffer for buffer in client.pre_establish_buffers
client.established = true
client.pre_establish_buffers = []
return
return
disconnect: (client, error)->
if client.is_post_watcher
ygopro.stoc_send_chat_to_room this, "#{client.name} 退出了观战" + if error then ": #{error}" else ''
index = _.indexOf(@watchers, client)
@watchers.splice(index, 1) unless index == -1
#client.room = null
else
index = _.indexOf(@players, client)
@players.splice(index, 1) unless index == -1
#log.info(@started,@disconnector,@random_type)
if @started and @disconnector != 'server' and @random_type and (client.pos < 4 or client.is_host)
ROOM_ban_player(client.name, client.ip, "强退")
if @players.length and !(@windbot and client.is_host)
ygopro.stoc_send_chat_to_room this, "#{client.name} 离开了游戏" + if error then ": #{error}" else ''
roomlist.update(this) if !@private and !@started and settings.modules.enable_websocket_roomlist
#client.room = null
else
@process.kill()
#client.room = null
this.delete()
return
# 网络连接
net.createServer (client) ->
client.ip = client.remoteAddress
connect_count = ROOM_connected_ip[client.ip] or 0
if client.ip != '::ffff:127.0.0.1'
connect_count++
ROOM_connected_ip[client.ip] = connect_count
#log.info "connect", client.ip, ROOM_connected_ip[client.ip]
# server stand for the connection to ygopro server process
server = new net.Socket()
client.server = server
client.setTimeout(300000) #5分钟
client.setTimeout(2000) #连接前超时2秒
#释放处理
# 释放处理
client.on 'close', (had_error) ->
#log.info "client closed", client.name, had_error
#log.info "client closed", client.name, had_error
room=ROOM_all[client.rid]
connect_count = ROOM_connected_ip[client.ip]
if connect_count > 0
connect_count--
ROOM_connected_ip[client.ip] = connect_count
#log.info "disconnect", client.ip, ROOM_connected_ip[client.ip]
tribute(client)
unless client.closed
client.closed = true
client.room.disconnect(client) if client.room
server.end()
room.disconnect(client) if room
server.destroy()
return
client.on 'error', (error)->
#log.info "client error", client.name, error
#log.info "client error", client.name, error
room=ROOM_all[client.rid]
connect_count = ROOM_connected_ip[client.ip]
if connect_count > 0
connect_count--
ROOM_connected_ip[client.ip] = connect_count
#log.info "err disconnect", client.ip, ROOM_connected_ip[client.ip]
tribute(client)
unless client.closed
client.closed = error
client.room.disconnect(client, error) if client.room
server.end()
room.disconnect(client, error) if room
server.destroy()
return
client.on 'timeout', ()->
server.end()
server.destroy()
return
server.on 'close', (had_error) ->
#log.info "server closed", client.name, had_error
#log.info "server closed", client.name, had_error
room=ROOM_all[client.rid]
#log.info "server close", client.ip, ROOM_connected_ip[client.ip]
tribute(server)
client.room.disconnector = 'server'
room.disconnector = 'server' if room
server.closed = true unless server.closed
unless client.closed
ygopro.stoc_send_chat(client, "服务器关闭了连接", 11)
client.end()
ygopro.stoc_send_chat(client, "服务器关闭了连接", ygopro.constants.COLORS.RED)
client.destroy()
return
server.on 'error', (error)->
#log.info "server error", client.name, error
#log.info "server error", client.name, error
room=ROOM_all[client.rid]
#log.info "server err close", client.ip, ROOM_connected_ip[client.ip]
tribute(server)
client.room.disconnector = 'server'
room.disconnector = 'server' if room
server.closed = error
unless client.closed
ygopro.stoc_send_chat(client, "服务器错误: #{error}", 11)
client.end()
ygopro.stoc_send_chat(client, "服务器错误: #{error}", ygopro.constants.COLORS.RED)
client.destroy()
return
###
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
client.destroy()
return
if settings.modules.enable_cloud_replay
client.open_cloud_replay= (err, replay)->
if err or !replay
ygopro.stoc_send_chat(client,"没有找到录像", 11)
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, "没有找到录像")
return
replay_buffer=new Buffer(replay.replay_buffer,'binary')
ygopro.stoc_send_chat(client,"正在观看云录像:R##{replay.replay_id} #{replay.player_names} #{replay.date_time}", 14)
redisdb.expire("replay:"+replay.replay_id, 60*60*48)
buffer=new Buffer(replay.replay_buffer,'binary')
zlib.unzip buffer, (err, replay_buffer) ->
if err
log.info "cloud replay unzip error: " + err
ygopro.stoc_send_chat(client, "播放录像出错", ygopro.constants.COLORS.RED)
client.destroy()
return
ygopro.stoc_send_chat(client, "正在观看云录像:R##{replay.replay_id} #{replay.player_names} #{replay.date_time}", ygopro.constants.COLORS.BABYBLUE)
client.write replay_buffer
client.end()
setTimeout (()->
client.destroy()
return), 5000
return
return
###
#需要重构
#客户端到服务端(ctos)协议分析
ctos_buffer = new Buffer(0)
ctos_message_length = 0
ctos_proto = 0
# 需要重构
# 客户端到服务端(ctos)协议分析
client.pre_establish_buffers = new Array()
client.on 'data', (data) ->
client.on 'data', (ctos_buffer) ->
if client.is_post_watcher
client.room.watcher.write data
room=ROOM_all[client.rid]
room.watcher.write ctos_buffer if room
else
ctos_buffer = Buffer.concat([ctos_buffer, data], ctos_buffer.length + data.length) #buffer的错误使用方式,好孩子不要学
#ctos_buffer = new Buffer(0)
ctos_message_length = 0
ctos_proto = 0
#ctos_buffer = Buffer.concat([ctos_buffer, data], ctos_buffer.length + data.length) #buffer的错误使用方式,好孩子不要学
datas = []
......@@ -157,15 +659,17 @@ net.createServer (client) ->
if ctos_buffer.length >= 2
ctos_message_length = ctos_buffer.readUInt16LE(0)
else
log.warn("bad ctos_buffer length", client.ip) unless ctos_buffer.length == 0
break
else if ctos_proto == 0
if ctos_buffer.length >= 3
ctos_proto = ctos_buffer.readUInt8(2)
else
log.warn("bad ctos_proto length", client.ip)
break
else
if ctos_buffer.length >= 2 + ctos_message_length
#console.log "CTOS", ygopro.constants.CTOS[ctos_proto]
#console.log "CTOS", ygopro.constants.CTOS[ctos_proto]
cancel = false
if ygopro.ctos_follows[ctos_proto]
b = ctos_buffer.slice(3, ctos_message_length - 1 + 3)
......@@ -182,13 +686,19 @@ net.createServer (client) ->
ctos_message_length = 0
ctos_proto = 0
else
log.warn("bad ctos_message length", client.ip, ctos_buffer.length, ctos_message_length, ctos_proto) if ctos_message_length != 17735
break
looplimit++
#log.info(looplimit)
if looplimit > 800
log.info("error ctos", client.name)
server.end()
if looplimit > 800 or ROOM_bad_ip[client.ip] > 5
log.info("error ctos", client.name, client.ip)
bad_ip_count = ROOM_bad_ip[client.ip]
if bad_ip_count
ROOM_bad_ip[client.ip] = bad_ip_count + 1
else
ROOM_bad_ip[client.ip] = 1
client.destroy()
break
if client.established
......@@ -198,16 +708,16 @@ net.createServer (client) ->
return
#服务端到客户端(stoc)
stoc_buffer = new Buffer(0)
# 服务端到客户端(stoc)
server.on 'data', (stoc_buffer)->
#stoc_buffer = new Buffer(0)
stoc_message_length = 0
stoc_proto = 0
server.on 'data', (data)->
stoc_buffer = Buffer.concat([stoc_buffer, data], stoc_buffer.length + data.length) #buffer的错误使用方式,好孩子不要学
#stoc_buffer = Buffer.concat([stoc_buffer, data], stoc_buffer.length + data.length) #buffer的错误使用方式,好孩子不要学
#unless ygopro.stoc_follows[stoc_proto] and ygopro.stoc_follows[stoc_proto].synchronous
client.write data
#client.write data
datas = []
looplimit = 0
......@@ -216,46 +726,61 @@ net.createServer (client) ->
if stoc_buffer.length >= 2
stoc_message_length = stoc_buffer.readUInt16LE(0)
else
log.warn("bad stoc_buffer length", client.ip) unless stoc_buffer.length == 0
break
else if stoc_proto == 0
if stoc_buffer.length >= 3
stoc_proto = stoc_buffer.readUInt8(2)
else
log.warn("bad stoc_proto length", client.ip)
break
else
if stoc_buffer.length >= 2 + stoc_message_length
#console.log "STOC", ygopro.constants.STOC[stoc_proto]
#console.log "STOC", ygopro.constants.STOC[stoc_proto]
cancel = false
stanzas = stoc_proto
if ygopro.stoc_follows[stoc_proto]
b = stoc_buffer.slice(3, stoc_message_length - 1 + 3)
if struct = ygopro.structs[ygopro.proto_structs.STOC[ygopro.constants.STOC[stoc_proto]]]
struct._setBuff(b)
if ygopro.stoc_follows[stoc_proto].synchronous
cancel = ygopro.stoc_follows[stoc_proto].callback b, _.clone(struct.fields), client, server
else
ygopro.stoc_follows[stoc_proto].callback b, _.clone(struct.fields), client, server
else
if ygopro.stoc_follows[stoc_proto].synchronous
cancel = ygopro.stoc_follows[stoc_proto].callback b, null, client, server
else
ygopro.stoc_follows[stoc_proto].callback b, null, client, server
datas.push stoc_buffer.slice(0, 2 + stoc_message_length) unless cancel
stoc_buffer = stoc_buffer.slice(2 + stoc_message_length)
stoc_message_length = 0
stoc_proto = 0
else
log.warn("bad stoc_message length", client.ip)
break
looplimit++
#log.info(looplimit)
if looplimit > 800
log.info("error stoc", client.name)
server.end()
server.destroy()
break
client.write buffer for buffer in datas
return
return
.listen settings.port, ->
log.info "server started", settings.port
return
#功能模块
# 功能模块
# return true to cancel a synchronous message
ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server)->
name = info.name.split("$")[0];
# checkmate use username$password, but here don't
# so remove the password
name = info.name.split("$")[0]
struct = ygopro.structs["CTOS_PlayerInfo"]
struct._setBuff(buffer)
struct.set("name", name)
......@@ -266,107 +791,69 @@ ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server)->
ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server)->
#log.info info
if settings.modules.stop
ygopro.stoc_send_chat(client, settings.modules.stop, 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, settings.modules.stop)
###
else if info.pass.toUpperCase()=="R"
ygopro.stoc_send_chat(client,"以下是您近期的云录像,密码处输入 R#录像编号 即可观看", 14)
redisdb.lrange client.remoteAddress+":replays", 0, 2, (err, result)=>
_.each result, (replay_id,id)=>
redisdb.hgetall "replay:"+replay_id, (err, replay)=>
ygopro.stoc_send_chat(client,"<#{id-0+1}> R##{replay_id} #{replay.player_names} #{replay.date_time}", 14)
else if info.pass.toUpperCase()=="R" and settings.modules.enable_cloud_replay
ygopro.stoc_send_chat(client,"以下是您近期的云录像,密码处输入 R#录像编号 即可观看", ygopro.constants.COLORS.BABYBLUE)
redisdb.lrange client.ip+":replays", 0, 2, (err, result)->
_.each result, (replay_id,id)->
redisdb.hgetall "replay:"+replay_id, (err, replay)->
if err or !replay
log.info "cloud replay getall error: " + err if err
return
ygopro.stoc_send_chat(client,"<#{id-0+1}> R##{replay_id} #{replay.player_names} #{replay.date_time}", ygopro.constants.COLORS.BABYBLUE)
return
return
#强行等待异步执行完毕_(:з」∠)_
setTimeout (()=>
return
# 强行等待异步执行完毕_(:з」∠)_
setTimeout (()->
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 2
}
client.end()), 500
client.destroy()
return), 500
else if info.pass[0...2].toUpperCase()=="R#"
else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.enable_cloud_replay
replay_id=info.pass.split("#")[1]
if (replay_id>0 and replay_id<=3)
redisdb.lindex client.remoteAddress+":replays", replay_id-1, (err, replay_id)=>
if (replay_id>0 and replay_id<=9)
redisdb.lindex client.ip+":replays", replay_id-1, (err, replay_id)->
if err or !replay_id
log.info "cloud replay replayid error: " + err if err
ygopro.stoc_die(client, "没有找到录像")
return
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
return
else if replay_id
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
else
ygopro.stoc_send_chat(client,"没有找到录像", 11)
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 2
}
client.end()
###
ygopro.stoc_die(client, "没有找到录像")
else if info.version != settings.version
ygopro.stoc_send_chat(client, settings.modules.update, 11)
else if info.pass.toUpperCase()=="W" and settings.modules.enable_cloud_replay
replay_id=Cloud_replay_ids[Math.floor(Math.random()*Cloud_replay_ids.length)]
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
else if info.version != settings.version and (info.version != 4921 or settings.version != 4922) #YGOMobile不更新,强行兼容
ygopro.stoc_send_chat(client, settings.modules.update, ygopro.constants.COLORS.RED)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 4
code: settings.version
}
client.end()
client.destroy()
else if !info.pass.length and !settings.modules.enable_random_duel
ygopro.stoc_send_chat(client, "房间名为空,请填写主机密码", 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
else if settings.modules.windbot and info.pass[0...2] == 'AI'
else if !info.pass.length and !settings.modules.enable_random_duel and !settings.modules.enable_windbot
ygopro.stoc_die(client, "房间名为空,请在主机密码处填写房间名")
if info.pass.length > 3 and info.pass[0...3] == 'AI#' or info.pass[0...3] == 'AI_'
name = info.pass.slice(3)
windbot = _.sample _.filter settings.modules.windbot, (w)->
w.name == name or w.deck == name
if !windbot
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Windbot Name)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
return
else
windbot = _.sample settings.modules.windbot
room = Room.find_or_create_by_name('AI#' + Math.random().toString()) # 这个 AI# 没有特殊作用, 仅作为标记
room.windbot = windbot
room.private = true
client.room = room
client.room.connect(client)
else if info.pass.length and settings.modules.mycard_auth
ygopro.stoc_send_chat(client, '正在读取用户信息...', 11)
else if info.pass.length and settings.modules.mycard_auth and info.pass[0...2] != 'AI'
ygopro.stoc_send_chat(client, '正在读取用户信息...', ygopro.constants.COLORS.BABYBLUE)
if info.pass.length <= 8
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Length)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, '主机密码不正确 (Invalid Length)')
return
buffer = new Buffer(info.pass[0...8], 'base64')
if buffer.length != 6
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Payload Length)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, '主机密码不正确 (Invalid Payload Length)')
return
check = (buf)->
......@@ -378,12 +865,7 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server)->
finish = (buffer)->
action = buffer.readUInt8(1) >> 4
if buffer != decrypted_buffer and action in [1, 2, 4]
ygopro.stoc_send_chat(client, '主机密码不正确 (Unauthorized)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, '主机密码不正确 (Unauthorized)')
return
# 1 create public room
......@@ -392,14 +874,9 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server)->
# 4 join match
switch action
when 1,2
name = crypto.createHash('md5').update(info.pass + client.name).digest('base64')[0...10].replace('+', '-').replace('/', '_');
if Room.find_by_name(name)
ygopro.stoc_send_chat(client, '主机密码不正确 (Already Existed)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
name = crypto.createHash('md5').update(info.pass + client.name).digest('base64')[0...10].replace('+', '-').replace('/', '_')
if ROOM_find_by_name(name)
ygopro.stoc_die(client, '主机密码不正确 (Already Existed)')
return
opt1 = buffer.readUInt8(2)
......@@ -423,31 +900,30 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server)->
room.private = action == 2
when 3
name = info.pass.slice(8)
room = Room.find_by_name(name)
room = ROOM_find_by_name(name)
if(!room)
ygopro.stoc_send_chat(client, '主机密码不正确 (Not Found)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, '主机密码不正确 (Not Found)')
return
when 4
room = Room.find_or_create_by_name('M#' + info.pass.slice(8))
room = ROOM_find_or_create_by_name('M#' + info.pass.slice(8))
room.private = true
else
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Action)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, '主机密码不正确 (Invalid Action)')
return
if !room
ygopro.stoc_die(client, "服务器已经爆满,请稍候再试")
else if room.error
ygopro.stoc_die(client, room.error)
else
#client.room = room
client.setTimeout(300000) #连接后超时5分钟
client.rid = _.indexOf(ROOM_all, room)
room.connect(client)
return
client.room = room
client.room.connect(client)
if id = users_cache[client.name]
secret = id % 65535 + 1;
secret = id % 65535 + 1
decrypted_buffer = new Buffer(6)
for i in [0, 2, 4]
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i)
......@@ -459,13 +935,13 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server)->
baseUrl: settings.modules.mycard_auth,
url: '/users/' + encodeURIComponent(client.name) + '.json',
qs:
api_key: 'dc7298a754828b3d26b709f035a0eeceb43e73cbd8c4fa8dec18951f8a95d2bc',
api_key: process.env.MYCARD_AUTH_KEY,
api_username: client.name,
skip_track_visit: true
json: true
, (error, response, body)->
if body and body.user
secret = body.user.id % 65535 + 1;
secret = body.user.id % 65535 + 1
decrypted_buffer = new Buffer(6)
for i in [0, 2, 4]
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i)
......@@ -475,102 +951,119 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server)->
# buffer != decrypted_buffer ==> auth failed
if !check(buffer)
ygopro.stoc_send_chat(client, '主机密码不正确 (Checksum Failed)', 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, '主机密码不正确 (Checksum Failed)')
return
users_cache[client.name] = body.user.id
finish(buffer)
else if info.pass.length && !Room.validate(info.pass)
#ygopro.stoc_send client, 'ERROR_MSG',{
# msg: 1
# code: 1 #这返错有问题,直接双ygopro直连怎么都正常,在这里就经常弹不出提示
#}
ygopro.stoc_send_chat(client, "房间密码不正确", 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
else if client.name == '[INCORRECT]' #模拟用户验证
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
else if _.indexOf(settings.BANNED_user, client.name) > -1 #账号被封
settings.BANNED_IP.push(client.remoteAddress)
log.info("BANNED USER LOGIN", client.name, client.remoteAddress)
ygopro.stoc_send_chat(client, "您的账号已被封禁", 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
else if _.indexOf(settings.BANNED_IP, client.remoteAddress) > -1 #IP被封
log.info("BANNED IP LOGIN", client.name, client.remoteAddress)
ygopro.stoc_send_chat(client, "您的账号已被封禁", 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
else if !client.name or client.name==""
ygopro.stoc_die(client, "请输入正确的用户名")
else if ROOM_connected_ip[client.ip] > 5
log.warn("MULTI LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "同时开启的客户端数量过多 " + client.ip)
else if _.indexOf(settings.ban.banned_user, client.name) > -1 #账号被封
settings.ban.banned_ip.push(client.ip)
log.warn("BANNED USER LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "您的账号已被封禁")
else if _.indexOf(settings.ban.banned_ip, client.ip) > -1 #IP被封
log.warn("BANNED IP LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "您的账号已被封禁")
else if _.any(settings.ban.badword_level3, (badword) ->
regexp = new RegExp(badword, 'i')
return name.match(regexp)
, name = client.name)
log.warn("BAD NAME LEVEL 3", client.name, client.ip)
ygopro.stoc_die(client, "您的用户名存在不适当的内容")
else if _.any(settings.ban.badword_level2, (badword) ->
regexp = new RegExp(badword, 'i')
return name.match(regexp)
, name = client.name)
log.warn("BAD NAME LEVEL 2", client.name, client.ip)
ygopro.stoc_die(client, "您的用户名存在不适当的内容")
else if _.any(settings.ban.badword_level1, (badword) ->
regexp = new RegExp(badword, 'i')
return name.match(regexp)
, name = client.name)
log.warn("BAD NAME LEVEL 1", client.name, client.ip)
ygopro.stoc_die(client, "您的用户名存在不适当的内容,请注意更改")
else if info.pass.length && !ROOM_validate(info.pass)
ygopro.stoc_die(client, "房间密码不正确")
else
#log.info 'join_game',info.pass, client.name
room = Room.find_or_create_by_name(info.pass, client.remoteAddress)
if info.version == 4921 and settings.version == 4922 #YGOMobile不更新,强行兼容
info.version = settings.version
struct = ygopro.structs["CTOS_JoinGame"]
struct._setBuff(buffer)
struct.set("version", info.version)
buffer = struct.buffer
#ygopro.stoc_send_chat(client, "您的版本号过低,可能出现未知问题,电脑用户请升级版本,YGOMobile用户请等待作者更新", ygopro.constants.COLORS.BABYBLUE)
#log.info 'join_game',info.pass, client.name
room = ROOM_find_or_create_by_name(info.pass, client.ip)
if !room
ygopro.stoc_send_chat(client, "服务器已经爆满,请稍候再试", 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, "服务器已经爆满,请稍候再试")
else if room.error
ygopro.stoc_send_chat(client, room.error, 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, room.error)
else if room.started
if settings.modules.post_start_watching
client.room = room
if settings.modules.enable_halfway_watch
client.setTimeout(300000) #连接后超时5分钟
client.rid = _.indexOf(ROOM_all, room)
client.is_post_watcher = true
ygopro.stoc_send_chat_to_room client.room, "#{client.name} 加入了观战"
client.room.watchers.push client
ygopro.stoc_send_chat client, "观战中", 14
for buffer in client.room.watcher_buffers
ygopro.stoc_send_chat_to_room(room, "#{client.name} 加入了观战")
room.watchers.push client
ygopro.stoc_send_chat(client, "观战中", ygopro.constants.COLORS.BABYBLUE)
for buffer in room.watcher_buffers
client.write buffer
else
ygopro.stoc_send_chat(client, "决斗已开始,不允许观战", 11)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
}
client.end()
ygopro.stoc_die(client, "决斗已开始,不允许观战")
else
client.room = room
client.room.connect(client)
client.setTimeout(300000) #连接后超时5分钟
client.rid = _.indexOf(ROOM_all, room)
room.connect(client)
return
ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server)->
#欢迎信息
return unless client.room
#欢迎信息
room=ROOM_all[client.rid]
return unless room
if settings.modules.welcome
ygopro.stoc_send_chat client, settings.modules.welcome
if client.room.welcome
ygopro.stoc_send_chat client, client.room.welcome, 14
ygopro.stoc_send_chat(client, settings.modules.welcome, ygopro.constants.COLORS.GREEN)
if room.welcome
ygopro.stoc_send_chat(client, room.welcome, ygopro.constants.COLORS.BABYBLUE)
#log.info(ROOM_all)
if !room.recorder
room.recorder = recorder = net.connect room.port, ->
ygopro.ctos_send recorder, 'PLAYER_INFO', {
name: "Marshtomp"
}
ygopro.ctos_send recorder, 'JOIN_GAME', {
version: settings.version,
gameid: 2577,
some_unknown_mysterious_fucking_thing: 0
pass: ""
}
ygopro.ctos_send recorder, 'HS_TOOBSERVER'
return
if settings.modules.post_start_watching and !client.room.watcher
client.room.watcher = watcher = net.connect client.room.port, ->
recorder.on 'data', (data)->
room=ROOM_all[client.rid]
return unless room and settings.modules.enable_cloud_replay
room.recorder_buffers.push data
return
recorder.on 'error', (error)->
return
if settings.modules.enable_halfway_watch and !room.watcher
room.watcher = watcher = net.connect room.port, ->
ygopro.ctos_send watcher, 'PLAYER_INFO', {
name: "the Big Brother"
}
......@@ -584,9 +1077,10 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server)->
return
watcher.on 'data', (data)->
return unless client.room
client.room.watcher_buffers.push data
for w in client.room.watchers
room=ROOM_all[client.rid]
return unless room
room.watcher_buffers.push data
for w in room.watchers
w.write data if w #a WTF fix
return
......@@ -595,9 +1089,8 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server)->
return
return
#登场台词
if settings.modules.dialogues
dialogues = {}
# 登场台词
load_dialogues = () ->
request
url: settings.modules.dialogues
json: true
......@@ -607,102 +1100,116 @@ if settings.modules.dialogues
else if error or !body
log.warn 'dialogues error', error, response
else
#log.info "dialogues loaded", _.size body
dialogues = body
nconf.myset(settings, "dialogues", body)
log.info "dialogues loaded", _.size body
return
return
if settings.modules.dialogues
load_dialogues()
ygopro.stoc_follow 'GAME_MSG', false, (buffer, info, client, server)->
room=ROOM_all[client.rid]
return unless room
msg = buffer.readInt8(0)
if msg >= 10 and msg < 30 #SELECT开头的消息
client.room.waiting_for_player = client
client.room.last_active_time = moment()
#log.info("#{ygopro.constants.MSG[msg]}等待#{client.room.waiting_for_player.name}")
room.waiting_for_player = client
room.last_active_time = moment()
#log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}")
#log.info 'MSG', ygopro.constants.MSG[msg]
if ygopro.constants.MSG[msg] == 'START'
playertype = buffer.readUInt8(1)
client.is_first = !(playertype & 0xf);
client.lp = client.room.hostinfo.start_lp
client.is_first = !(playertype & 0xf)
client.lp = room.hostinfo.start_lp
#ygopro.stoc_send_chat_to_room(client.room, "LP跟踪调试信息: #{client.name} 初始LP #{client.lp}")
###
if ygopro.constants.MSG[msg] == 'WIN' and _.startsWith(client.room.name, 'M#') and client.is_host
#ygopro.stoc_send_chat_to_room(room, "LP跟踪调试信息: #{client.name} 初始LP #{client.lp}")
if ygopro.constants.MSG[msg] == 'WIN' and client.is_host
pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first or pos == 2
reason = buffer.readUInt8(2)
#log.info {winner: pos, reason: reason}
client.room.duels.push {winner: pos, reason: reason}
###
#room.duels.push {winner: pos, reason: reason}
room.winner = pos
#lp跟踪
if ygopro.constants.MSG[msg] == 'DAMAGE' and client.is_host
pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first
val = buffer.readInt32LE(2)
client.room.dueling_players[pos].lp -= val
if 0 < client.room.dueling_players[pos].lp <= 100
ygopro.stoc_send_chat_to_room(client.room, "你的生命已经如风中残烛了!", 15)
room.dueling_players[pos].lp -= val
if 0 < room.dueling_players[pos].lp <= 100
ygopro.stoc_send_chat_to_room(room, "你的生命已经如风中残烛了!", ygopro.constants.COLORS.PINK)
if ygopro.constants.MSG[msg] == 'RECOVER' and client.is_host
pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first
val = buffer.readInt32LE(2)
client.room.dueling_players[pos].lp += val
room.dueling_players[pos].lp += val
if ygopro.constants.MSG[msg] == 'LPUPDATE' and client.is_host
pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first
val = buffer.readInt32LE(2)
client.room.dueling_players[pos].lp = val
room.dueling_players[pos].lp = val
if ygopro.constants.MSG[msg] == 'PAY_LPCOST' and client.is_host
pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first
val = buffer.readInt32LE(2)
client.room.dueling_players[pos].lp -= val
if 0 < client.room.dueling_players[pos].lp <= 100
ygopro.stoc_send_chat_to_room(client.room, "背水一战!", 15)
room.dueling_players[pos].lp -= val
if 0 < room.dueling_players[pos].lp <= 100
ygopro.stoc_send_chat_to_room(room, "背水一战!", ygopro.constants.COLORS.PINK)
#登场台词
if settings.modules.dialogues
if ygopro.constants.MSG[msg] == 'SUMMONING' or ygopro.constants.MSG[msg] == 'SPSUMMONING'
card = buffer.readUInt32LE(1)
if dialogues[card]
for line in _.lines dialogues[card][Math.floor(Math.random() * dialogues[card].length)]
ygopro.stoc_send_chat client, line, 15
if settings.dialogues[card]
for line in _.lines settings.dialogues[card][Math.floor(Math.random() * settings.dialogues[card].length)]
ygopro.stoc_send_chat(client, line, ygopro.constants.COLORS.PINK)
return
#房间管理
ygopro.ctos_follow 'HS_KICK', true, (buffer, info, client, server)->
return unless client.room
for player in client.room.players
room=ROOM_all[client.rid]
return unless room
for player in room.players
if player and player.pos == info.pos and player != client
ygopro.stoc_send_chat_to_room client.room, "#{player.name} 被请出了房间", 11
client.kick_count = if client.kick_count then client.kick_count+1 else 1
if client.kick_count>=5
ygopro.stoc_send_chat_to_room(room, "#{client.name} 被系统请出了房间", ygopro.constants.COLORS.RED)
ROOM_ban_player(player.name, player.ip, "挂房间")
client.destroy()
return true
ygopro.stoc_send_chat_to_room(room, "#{player.name} 被请出了房间", ygopro.constants.COLORS.RED)
return false
ygopro.stoc_follow 'TYPE_CHANGE', false, (buffer, info, client, server)->
selftype = info.type & 0xf;
is_host = ((info.type >> 4) & 0xf) != 0;
selftype = info.type & 0xf
is_host = ((info.type >> 4) & 0xf) != 0
client.is_host = is_host
client.pos = selftype
#console.log "TYPE_CHANGE to #{client.name}:", info, selftype, is_host
return
ygopro.stoc_follow 'HS_PLAYER_CHANGE', false, (buffer, info, client, server)->
return unless client.room and client.room.max_player and client.is_host
pos = info.status >> 4;
is_ready = (info.status & 0xf) == 9;
if pos < client.room.max_player
client.room.ready_player_count_without_host = 0
for player in client.room.players
room=ROOM_all[client.rid]
return unless room and room.max_player and client.is_host
pos = info.status >> 4
is_ready = (info.status & 0xf) == 9
if pos < room.max_player
room.ready_player_count_without_host = 0
for player in room.players
if player.pos == pos
player.is_ready = is_ready
unless player.is_host
client.room.ready_player_count_without_host += player.is_ready
if client.room.ready_player_count_without_host >= client.room.max_player - 1
#log.info "all ready"
setTimeout (()-> wait_room_start(client.room, 20);return), 1000
room.ready_player_count_without_host += player.is_ready
if room.ready_player_count_without_host >= room.max_player - 1
#log.info "all ready"
setTimeout (()-> wait_room_start(ROOM_all[client.rid], 20);return), 1000
return
wait_room_start = (room, time)->
......@@ -710,154 +1217,327 @@ wait_room_start = (room, time)->
time -= 1
if time
unless time % 5
ygopro.stoc_send_chat_to_room room, "#{if time <= 9 then ' ' else ''}#{time}秒后房主若不开始游戏将被请出房间", if time <= 9 then 11 else 8
ygopro.stoc_send_chat_to_room(room, "#{if time <= 9 then ' ' else ''}#{time}秒后房主若不开始游戏将被请出房间", if time <= 9 then ygopro.constants.COLORS.RED else ygopro.constants.COLORS.LIGHTBLUE)
setTimeout (()-> wait_room_start(room, time);return), 1000
else
for player in room.players
if player and player.is_host
Room.ban_player(player.name, player.ip, "挂房间")
ygopro.stoc_send_chat_to_room room, "#{player.name} 被系统请出了房间", 11
player.end()
ROOM_ban_player(player.name, player.ip, "挂房间")
ygopro.stoc_send_chat_to_room(room, "#{player.name} 被系统请出了房间", ygopro.constants.COLORS.RED)
player.destroy()
return
#tip
ygopro.stoc_send_random_tip = (client)->
ygopro.stoc_send_chat client, "Tip: " + tips[Math.floor(Math.random() * tips.length)] if tips
ygopro.stoc_send_chat(client, "Tip: " + settings.tips[Math.floor(Math.random() * settings.tips.length)]) if settings.modules.tips
return
ygopro.stoc_send_random_tip_to_room = (room)->
ygopro.stoc_send_chat_to_room room, "Tip: " + tips[Math.floor(Math.random() * tips.length)] if tips
return
setInterval ()->
for room in Room.all
ygopro.stoc_send_random_tip_to_room(room) unless room and room.started
ygopro.stoc_send_chat_to_room(room, "Tip: " + settings.tips[Math.floor(Math.random() * settings.tips.length)]) if settings.modules.tips
return
, 30000
tips = null
if settings.modules.tips
load_tips = ()->
request
url: settings.modules.tips
json: true
, (error, response, body)->
tips = body
#log.info "tips loaded", tips.length
if _.isString body
log.warn "tips bad json", body
else if error or !body
log.warn 'tips error', error, response
else
nconf.myset(settings, "tips", body)
log.info "tips loaded", settings.tips.length
return
return
if settings.modules.tips
load_tips()
setInterval ()->
for room in ROOM_all when room and room.established
ygopro.stoc_send_random_tip_to_room(room) unless room and room.started
return
, 30000
if settings.modules.mycard_auth and process.env.MYCARD_AUTH_DATABASE
pg = require('pg');
pg = require('pg')
pg.connect process.env.MYCARD_AUTH_DATABASE, (error, client, done)->
throw error if error
client.query 'SELECT username, id from users', (error, result)->
throw error if error
done();
done()
for row in result.rows
users_cache[row.username] = row.id
console.log("users loaded", _.keys(users_cache).length)
return
return
ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server)->
return unless client.room
unless client.room.started #first start
client.room.started = true
roomlist.delete client.room.name if settings.modules.enable_websocket_roomlist and not client.room.private
#client.room.duels = []
client.room.dueling_players = []
for player in client.room.players when player.pos != 7
client.room.dueling_players[player.pos] = player
client.room.player_datas.push ip: player.remoteAddress, name: player.name
if client.room.windbot
client.room.dueling_players[1 - player.pos] = {}
room=ROOM_all[client.rid]
return unless room
unless room.started #first start
room.started = true
roomlist.delete room.name if settings.modules.enable_websocket_roomlist and not room.private
#room.duels = []
room.dueling_players = []
for player in room.players when player.pos != 7
room.dueling_players[player.pos] = player
room.player_datas.push ip: player.ip, name: player.name
if settings.modules.tips
ygopro.stoc_send_random_tip(client)
if settings.modules.enable_deck_log and client.main and client.main.length and not client.deck_saved
deck_text = '#ygosrv233 deck log\r\n#main\r\n' + client.main.join('\r\n') + '\r\n!side\r\n' + client.side.join('\r\n') + '\r\n'
deck_name = moment().format('YYYY-MM-DD HH-mm-ss') + ' ' + room.port + ' ' + client.pos + ' ' + client.name.replace(/\//g, '_')
fs.writeFile 'decks_save\/' + deck_name + '.ydk', deck_text, 'utf-8', (err) ->
if err
log.warn 'DECK SAVE ERROR', err
client.deck_saved = true
return
ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server)->
cancel = _.startsWith(_.trim(info.msg), "/")
client.room.last_active_time = moment() unless cancel or not client.room.random_type
switch _.trim(info.msg)
when '/ping'
execFile 'ss', ['-it', "dst #{client.remoteAddress}:#{client.remotePort}"], (error, stdout, stderr)->
if error
ygopro.stoc_send_chat_to_room client.room, error
else
line = _.lines(stdout)[2]
if line.indexOf('rtt') != -1
ygopro.stoc_send_chat_to_room client.room, line
else
#log.warn 'ping', stdout
ygopro.stoc_send_chat_to_room client.room, stdout
return
room=ROOM_all[client.rid]
return unless room
msg = _.trim(info.msg)
cancel = _.startsWith(msg, "/")
room.last_active_time = moment() unless cancel or not room.random_type
cmd = msg.split(' ')
switch cmd[0]
when '/help'
ygopro.stoc_send_chat(client, "YGOSrv233 指令帮助")
ygopro.stoc_send_chat(client, "/help 显示这个帮助信息")
ygopro.stoc_send_chat(client, "/roomname 显示当前房间的名字")
ygopro.stoc_send_chat(client, "/ai 添加一个AI,/ai 角色名 可指定添加的角色") if settings.modules.enable_windbot
ygopro.stoc_send_chat(client, "/tip 显示一条提示") if settings.modules.tips
when '/tip'
ygopro.stoc_send_random_tip(client) if settings.modules.tips
when '/roomname'
ygopro.stoc_send_chat(client, "您当前的房间名是 " + client.room.name) if client.room
when '/ai'
if settings.modules.enable_windbot
if name = cmd[1]
windbot = _.sample _.filter settings.modules.windbots, (w)->
w.name == name or w.deck == name
if !windbot
ygopro.stoc_send_chat(client, "未找到该AI角色或卡组", ygopro.constants.COLORS.RED)
return
else
windbot = _.sample settings.modules.windbots
room.add_windbot(windbot)
when '/test'
ygopro.stoc_send_hint_card_to_room(client.room, 2333365)
when '/roomname'
ygopro.stoc_send_chat(client, "您当前的房间名是 " + room.name, ygopro.constants.COLORS.BABYBLUE) if room
#when '/test'
# ygopro.stoc_send_hint_card_to_room(room, 2333365)
if !(room and room.random_type)
return cancel
ygopro.ctos_follow 'UPDATE_DECK', false, (buffer, info, client, server)->
#log.info info
main = (info.deckbuf[i] for i in [0...info.mainc])
side = (info.deckbuf[i] for i in [info.mainc...info.mainc + info.sidec])
client.main = main
client.side = side
if client.abuse_count>=5
log.warn "BANNED CHAT", client.name, client.ip, msg
ygopro.stoc_send_chat(client, "您已被禁言!", ygopro.constants.COLORS.RED)
return true
oldmsg = msg
if (_.any(settings.ban.badword_level3, (badword) ->
regexp = new RegExp(badword, 'i')
return msg.match(regexp)
, msg))
log.warn "BAD WORD LEVEL 3", client.name, client.ip, oldmsg
cancel = true
if client.abuse_count>0
ygopro.stoc_send_chat(client, "您的发言存在严重不适当的内容,禁止您使用随机对战功能!", ygopro.constants.COLORS.RED)
ROOM_ban_player(client.name, client.ip, "发言违规")
ROOM_ban_player(client.name, client.ip, "发言违规", 3)
client.destroy()
return true
else
client.abuse_count=client.abuse_count+4
ygopro.stoc_send_chat(client, "您的发言存在不适当的内容,发送失败!", ygopro.constants.COLORS.RED)
else if (_.any(settings.ban.badword_level2, (badword) ->
regexp = new RegExp(badword, 'i')
return msg.match(regexp)
, msg))
log.warn "BAD WORD LEVEL 2", client.name, client.ip, oldmsg
client.abuse_count=client.abuse_count+3
ygopro.stoc_send_chat(client, "您的发言存在不适当的内容,发送失败!", ygopro.constants.COLORS.RED)
cancel = true
else
_.each(settings.ban.badword_level1, (badword) ->
#log.info msg
regexp = new RegExp(badword, "ig")
msg = msg.replace(regexp, "**")
return
, msg)
if oldmsg != msg
log.warn "BAD WORD LEVEL 1", client.name, client.ip, oldmsg
client.abuse_count=client.abuse_count+1
ygopro.stoc_send_chat(client, "请使用文明用语!")
struct = ygopro.structs["chat"]
struct._setBuff(buffer)
struct.set("msg", msg)
buffer = struct.buffer
else if (_.any(settings.ban.badword_level0, (badword) ->
regexp = new RegExp(badword, 'i')
return msg.match(regexp)
, msg))
log.info "BAD WORD LEVEL 0", client.name, client.ip, oldmsg
if client.abuse_count>=5
ygopro.stoc_send_chat_to_room(room, "#{client.name} 已被禁言!", ygopro.constants.COLORS.RED)
ROOM_ban_player(client.name, client.ip, "发言违规")
return cancel
ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server)->
room=ROOM_all[client.rid]
return false unless room
#log.info info
buff_main = (info.deckbuf[i] for i in [0...info.mainc])
buff_side = (info.deckbuf[i] for i in [info.mainc...info.mainc + info.sidec])
client.main = buff_main
client.side = buff_side
if room.random_type
if client.is_host
room.waiting_for_player = room.waiting_for_player2
room.last_active_time = moment()
else if !room.started and room.hostinfo.mode == 1 and settings.modules.tournament_mode.enabled
struct = ygopro.structs["deck"]
struct._setBuff(buffer)
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
buffer = struct.buffer
found_deck=false
decks=fs.readdirSync(settings.modules.tournament_mode.deck_path)
for deck in decks
if _.endsWith(deck, client.name+".ydk")
found_deck=deck
if _.endsWith(deck, client.name+".ydk.ydk")
found_deck=deck
if found_deck
deck_text=fs.readFileSync(settings.modules.tournament_mode.deck_path+found_deck,{encoding:"ASCII"})
deck_array=deck_text.split("\n")
deck_main=[]
deck_side=[]
current_deck=deck_main
for line in deck_array
if line.indexOf("!side")>=0
current_deck=deck_side
card=parseInt(line)
current_deck.push(card) unless isNaN(card)
if _.isEqual(buff_main, deck_main) and _.isEqual(buff_side, deck_side)
deckbuf=deck_main.concat(deck_side)
struct.set("mainc", deck_main.length)
struct.set("sidec", deck_side.length)
struct.set("deckbuf", deckbuf)
buffer = struct.buffer
#log.info("deck ok: " + client.name)
ygopro.stoc_send_chat(client, "成功使用卡组 #{found_deck} 参加比赛。", ygopro.constants.COLORS.BABYBLUE)
else
#log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
ygopro.stoc_send_chat(client, "您的卡组与报名卡组 #{found_deck} 不符。注意卡组不能有包括卡片顺序在内的任何修改。", ygopro.constants.COLORS.RED)
else
#log.info("player deck not found: " + client.name)
ygopro.stoc_send_chat(client, "#{client.name},没有找到您的报名信息,请确定您使用昵称与报名ID一致。", ygopro.constants.COLORS.RED)
return false
ygopro.ctos_follow 'RESPONSE', false, (buffer, info, client, server)->
return unless client.room and client.room.random_type
client.room.last_active_time = moment()
room=ROOM_all[client.rid]
return unless room and room.random_type
room.last_active_time = moment()
return
ygopro.ctos_follow 'HAND_RESULT', false, (buffer, info, client, server)->
return unless client.room and client.room.random_type
room=ROOM_all[client.rid]
return unless room and room.random_type
if client.is_host
client.room.waiting_for_player = client.room.waiting_for_player2
client.room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's')
room.waiting_for_player = room.waiting_for_player2
room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's')
return
ygopro.ctos_follow 'TP_RESULT', false, (buffer, info, client, server)->
return unless client.room and client.room.random_type
client.room.last_active_time = moment()
room=ROOM_all[client.rid]
return unless room and room.random_type
room.last_active_time = moment()
return
ygopro.stoc_follow 'SELECT_HAND', false, (buffer, info, client, server)->
return unless client.room and client.room.random_type
room=ROOM_all[client.rid]
return unless room and room.random_type
if client.is_host
client.room.waiting_for_player = client
room.waiting_for_player = client
else
client.room.waiting_for_player2 = client
client.room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's')
room.waiting_for_player2 = client
room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's')
return
ygopro.stoc_follow 'SELECT_TP', false, (buffer, info, client, server)->
return unless client.room and client.room.random_type
client.room.waiting_for_player = client
client.room.last_active_time = moment()
room=ROOM_all[client.rid]
return unless room and room.random_type
room.waiting_for_player = client
room.last_active_time = moment()
return
setInterval ()->
for room in Room.all when room and room.started and room.random_type and room.last_active_time and room.waiting_for_player
ygopro.stoc_follow 'CHANGE_SIDE', false, (buffer, info, client, server)->
room=ROOM_all[client.rid]
return unless room and room.random_type
if client.is_host
room.waiting_for_player = client
else
room.waiting_for_player2 = client
room.last_active_time = moment()
return
ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server)->
room=ROOM_all[client.rid]
return settings.modules.tournament_mode.enabled unless room
if settings.modules.enable_cloud_replay and room.random_type
Cloud_replay_ids.push room.cloud_replay_id
if settings.modules.tournament_mode.enabled
if client.is_host
duellog = {
time: moment().format('YYYY-MM-DD HH:mm:ss'),
name: room.name,
roomid: room.port.toString(),
cloud_replay_id: "R#"+room.cloud_replay_id,
players: (for player in room.players
name: player.name,
winner: player.pos == room.winner
)
}
settings.modules.tournament_mode.duel_log.push duellog
nconf.myset(settings, "modules:tournament_mode:duel_log", settings.modules.tournament_mode.duel_log)
if settings.modules.enable_cloud_replay
ygopro.stoc_send_chat(client, "本场比赛云录像:R##{room.cloud_replay_id}。将于本局结束后可播放。", ygopro.constants.COLORS.BABYBLUE)
return true
else
return false
if settings.modules.enable_random_duel
setInterval ()->
for room in ROOM_all when room and room.started and room.random_type and room.last_active_time and room.waiting_for_player
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.hang_timeout
room.last_active_time = moment()
Room.ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "挂机")
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} 被系统请出了房间", 11)
room.waiting_for_player.server.end()
ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "挂机")
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} 被系统请出了房间", ygopro.constants.COLORS.RED)
room.waiting_for_player.server.destroy()
else if time_passed >= (settings.modules.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} 已经很久没有操作了,若继续挂机,将于#{settings.modules.hang_timeout - time_passed}秒后被请出房间", 11)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} 已经很久没有操作了,若继续挂机,将于#{settings.modules.hang_timeout - time_passed}秒后被请出房间", ygopro.constants.COLORS.RED)
return
, 1000
if settings.modules.spawn_windbot
windbot_process = spawn 'mono', ['WindBot.exe', settings.modules.windbot_port], {cwd: 'windbot'}
windbot_process.on 'error', (err)->
log.warn 'WindBot ERROR', err
return
windbot_process.on 'exit', (code)->
log.warn 'WindBot EXIT', code
return
windbot_process.stdout.setEncoding('utf8')
windbot_process.stdout.on 'data', (data)->
log.info 'WindBot:', data
return
windbot_process.stderr.on 'data', (data)->
log.warn 'WindBot Error:', data
return
, 1000
#http
if settings.modules.http
......@@ -868,12 +1548,12 @@ if settings.modules.http
pass_validated = u.query.pass == settings.modules.http.password
if u.pathname == '/api/getrooms'
if u.query.pass and !pass_validated
response.writeHead(200);
if !pass_validated
response.writeHead(200)
response.end(u.query.callback + '( {"rooms":[{"roomid":"0","roomname":"密码错误","needpass":"true"}]} );')
else
response.writeHead(200);
roomsjson = JSON.stringify rooms: (for room in Room.all when room.established
response.writeHead(200)
roomsjson = JSON.stringify rooms: (for room in ROOM_all when room and room.established
pid: room.process.pid.toString(),
roomid: room.port.toString(),
roomname: if pass_validated then room.name else room.name.split('$', 2)[0],
......@@ -887,42 +1567,66 @@ if settings.modules.http
)
response.end(u.query.callback + "( " + roomsjson + " );")
else if u.pathname == '/api/duellog' and settings.modules.tournament_mode.enabled
if !pass_validated
response.writeHead(200)
response.end("密码错误")
return
else
response.writeHead(200)
duellog = JSON.stringify settings.modules.tournament_mode.duel_log
response.end(u.query.callback + "( " + duellog + " );")
else if u.pathname == '/api/message'
if !pass_validated
response.writeHead(200);
response.end(u.query.callback + "( '密码错误', 0 );");
response.writeHead(200)
response.end(u.query.callback + "( ['密码错误', 0] );")
return
if u.query.shout
for room in Room.all
ygopro.stoc_send_chat_to_room(room, u.query.shout, 16)
for room in ROOM_all when room and room.established
ygopro.stoc_send_chat_to_room(room, u.query.shout, ygopro.constants.COLORS.YELLOW)
response.writeHead(200)
response.end(u.query.callback + "( 'shout ok', '" + u.query.shout + "' );")
response.end(u.query.callback + "( ['shout ok', '" + u.query.shout + "'] );")
else if u.query.stop
if u.query.stop == 'false'
u.query.stop = false
settings.modules.stop = u.query.stop
response.writeHead(200)
response.end(u.query.callback + "( 'stop ok', '" + u.query.stop + "' );")
response.end(u.query.callback + "( ['stop ok', '" + u.query.stop + "'] );")
else if u.query.welcome
settings.modules.welcome = u.query.welcome
nconf.myset(settings, 'modules:welcome', u.query.welcome)
response.writeHead(200)
response.end(u.query.callback + "( ['welcome ok', '" + u.query.welcome + "'] );")
else if u.query.getwelcome
response.writeHead(200)
response.end(u.query.callback + "( ['get ok', '" + settings.modules.welcome + "'] );")
else if u.query.loadtips
load_tips()
response.writeHead(200)
response.end(u.query.callback + "( ['loading tip', '" + settings.modules.tips + "'] );")
else if u.query.loaddialogues
load_dialogues()
response.writeHead(200)
response.end(u.query.callback + "( 'welcome ok', '" + u.query.welcome + "' );")
response.end(u.query.callback + "( ['loading dialogues', '" + settings.modules.dialogues + "'] );")
else if u.query.ban
settings.BANNED_user.push(u.query.ban)
ban_user(u.query.ban)
response.writeHead(200)
response.end(u.query.callback + "( 'ban ok', '" + u.query.ban + "' );")
response.end(u.query.callback + "( ['ban ok', '" + u.query.ban + "'] );")
else
response.writeHead(404);
response.end();
response.writeHead(404)
response.end()
else
response.writeHead(404);
response.end();
response.writeHead(404)
response.end()
return
http_server = http.createServer(requestListener)
......
// Generated by CoffeeScript 1.10.0
(function() {
var Graveyard, Room, _, bunyan, crypto, debug, dialogues, execFile, fs, http, http_server, https, https_server, list, log, moment, net, options, os, path, pg, request, requestListener, roomlist, settings, tips, tribute, url, users_cache, wait_room_start, ygopro;
var Cloud_replay_ids, Graveyard, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_port, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_players_banned, ROOM_players_oppentlist, ROOM_validate, Room, _, ban_user, bunyan, crypto, date, defaultconfig, execFile, fs, get_memory_usage, http, http_server, https, https_server, list, load_dialogues, load_tips, log, moment, nconf, net, options, os, path, pg, redis, redisdb, request, requestListener, roomlist, settings, spawn, spawnSync, tribute, url, users_cache, wait_room_start, windbot_process, ygopro, zlib;
net = require('net');
......@@ -18,6 +18,10 @@
execFile = require('child_process').execFile;
spawn = require('child_process').spawn;
spawnSync = require('child_process').spawnSync;
_ = require('underscore');
_.str = require('underscore.string');
......@@ -28,15 +32,84 @@
bunyan = require('bunyan');
log = bunyan.createLogger({
name: "mycard"
});
moment = require('moment');
settings = require('./config.json');
moment.locale('zh-cn', {
relativeTime: {
future: '%s内',
past: '%s前',
s: '%d秒',
m: '1分钟',
mm: '%d分钟',
h: '1小时',
hh: '%d小时',
d: '1天',
dd: '%d天',
M: '1个月',
MM: '%d个月',
y: '1年',
yy: '%d年'
}
});
nconf = require('nconf');
nconf.file('./config.user.json');
settings.BANNED_user = [];
defaultconfig = require('./config.json');
settings.BANNED_IP = [];
nconf.defaults(defaultconfig);
settings.modules.hang_timeout = 90;
settings = global.settings = nconf.get();
nconf.myset = function(settings, path, val) {
var key, target;
nconf.set(path, val);
nconf.save();
if (_.isString(val)) {
log.info("setting changed", path, val);
}
path = path.split(':');
if (path.length === 0) {
settings[path[0]] = val;
} else {
target = settings;
while (path.length > 1) {
key = path.shift();
target = target[key];
}
key = path.shift();
target[key] = val;
}
};
ban_user = function(name) {
var bad_ip, k, l, len, len1, player, ref, room;
settings.ban.banned_user.push(name);
nconf.myset(settings, "ban:banned_user", settings.ban.banned_user);
bad_ip = 0;
for (k = 0, len = ROOM_all.length; k < len; k++) {
room = ROOM_all[k];
if (room && room.established) {
ref = room.players;
for (l = 0, len1 = ref.length; l < len1; l++) {
player = ref[l];
if (player && (player.name === name || player.ip === bad_ip)) {
bad_ip = player.ip;
ROOM_bad_ip.push(player.ip);
settings.ban.banned_ip.push(player.ip);
ygopro.stoc_send_chat_to_room(room, player.name + " 被系统请出了房间", ygopro.constants.COLORS.RED);
player.destroy();
continue;
}
}
}
}
};
settings.version = parseInt(fs.readFileSync('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16');
......@@ -46,6 +119,10 @@
results = [];
for (k = 0, len = ref.length; k < len; k++) {
list = ref[k];
date = list.match(/!([\d\.]+)/);
if (!date) {
continue;
}
results.push({
date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"),
tcg: list.indexOf('TCG') !== -1
......@@ -54,37 +131,47 @@
return results;
})();
if (settings.modules.enable_cloud_replay) {
redis = require('redis');
zlib = require('zlib');
redisdb = redis.createClient({
host: "127.0.0.1",
port: settings.modules.redis_port
});
redisdb.on('error', function(err) {
log.warn(err);
});
}
if (settings.modules.enable_windbot) {
settings.modules.windbot = require('./windbot/bots.json').windbots;
settings.modules.windbots = require('./windbot/bots.json').windbots;
}
ygopro = require('./ygopro.js');
Room = require('./room.js');
if (settings.modules.enable_websocket_roomlist) {
roomlist = require('./roomlist.js');
}
users_cache = {};
debug = false;
log = null;
if (process.argv[2] === '--debug') {
settings.port++;
if (settings.modules.http) {
settings.modules.http.port++;
}
log = bunyan.createLogger({
name: "mycard-debug"
});
get_memory_usage = function() {
var actualFree, buffers, cached, free, line, lines, percentUsed, prc_free, total;
prc_free = spawnSync("free", []);
if (prc_free.stdout) {
lines = prc_free.stdout.toString().split(/\n/g);
line = lines[1].split(/\s+/);
total = parseInt(line[1], 10);
free = parseInt(line[3], 10);
buffers = parseInt(line[5], 10);
cached = parseInt(line[6], 10);
actualFree = free + buffers + cached;
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2));
} else {
log = bunyan.createLogger({
name: "mycard"
});
percentUsed = 0;
}
return percentUsed;
};
Graveyard = [];
......@@ -111,81 +198,639 @@
Graveyard = [];
}, 3000);
Cloud_replay_ids = [];
ROOM_all = [];
ROOM_players_oppentlist = {};
ROOM_players_banned = [];
ROOM_connected_ip = {};
ROOM_bad_ip = {};
ROOM_ban_player = function(name, ip, reason, countadd) {
var bannedplayer, bantime;
if (countadd == null) {
countadd = 1;
}
bannedplayer = _.find(ROOM_players_banned, function(bannedplayer) {
return ip === bannedplayer.ip;
});
if (bannedplayer) {
bannedplayer.count = bannedplayer.count + countadd;
bantime = bannedplayer.count > 3 ? Math.pow(2, bannedplayer.count - 3) * 2 : 0;
bannedplayer.time = moment() < bannedplayer.time ? moment(bannedplayer.time).add(bantime, 'm') : moment().add(bantime, 'm');
if (!_.find(bannedplayer.reasons, function(bannedreason) {
return bannedreason === reason;
})) {
bannedplayer.reasons.push(reason);
}
bannedplayer.need_tip = true;
} else {
bannedplayer = {
"ip": ip,
"time": moment(),
"count": countadd,
"reasons": [reason],
"need_tip": true
};
ROOM_players_banned.push(bannedplayer);
}
};
ROOM_find_or_create_by_name = function(name, player_ip) {
var room, uname;
uname = name.toUpperCase();
if (settings.modules.enable_windbot && (uname.slice(0, 2) === 'AI' || (!settings.modules.enable_random_duel && uname === ''))) {
return ROOM_find_or_create_ai(name);
}
if (settings.modules.enable_random_duel && (uname === '' || uname === 'S' || uname === 'M' || uname === 'T')) {
return ROOM_find_or_create_random(uname, player_ip);
}
if (room = ROOM_find_by_name(name)) {
return room;
} else if (get_memory_usage() >= 90) {
return null;
} else {
return new Room(name);
}
};
ROOM_find_or_create_random = function(type, player_ip) {
var bannedplayer, max_player, name, playerbanned, result;
bannedplayer = _.find(ROOM_players_banned, function(bannedplayer) {
return player_ip === bannedplayer.ip;
});
if (bannedplayer) {
if (bannedplayer.count > 6 && moment() < bannedplayer.time) {
return {
"error": "因为您近期在游戏中多次" + (bannedplayer.reasons.join('')) + ",您已被禁止使用随机对战功能,将在" + (moment(bannedplayer.time).fromNow(true)) + "后解封"
};
}
if (bannedplayer.count > 3 && moment() < bannedplayer.time && bannedplayer.need_tip) {
bannedplayer.need_tip = false;
return {
"error": "因为您近期在游戏中" + (bannedplayer.reasons.join('')) + ",在" + (moment(bannedplayer.time).fromNow(true)) + "内您随机对战时只能遇到其他违规玩家"
};
} else if (bannedplayer.need_tip) {
bannedplayer.need_tip = false;
return {
"error": "系统检测到您近期在游戏中" + (bannedplayer.reasons.join('')) + ",若您违规超过3次,将受到惩罚"
};
} else if (bannedplayer.count > 2) {
bannedplayer.need_tip = true;
}
}
max_player = type === 'T' ? 4 : 2;
playerbanned = bannedplayer && bannedplayer.count > 3 && moment() < bannedplayer.time;
result = _.find(ROOM_all, function(room) {
return room && room.random_type !== '' && !room.started && ((type === '' && room.random_type !== 'T') || room.random_type === type) && room.get_playing_player().length < max_player && (room.get_host() === null || room.get_host().ip !== ROOM_players_oppentlist[player_ip]) && (playerbanned === room.deprecated);
});
if (result) {
result.welcome = '对手已经在等你了,开始决斗吧!';
} else if (get_memory_usage() < 90) {
type = type ? type : 'S';
name = type + ',RANDOM#' + Math.floor(Math.random() * 100000);
result = new Room(name);
result.random_type = type;
result.max_player = max_player;
result.welcome = '已建立随机对战房间,正在等待对手!';
result.deprecated = playerbanned;
} else {
return null;
}
if (result.random_type === 'M') {
result.welcome = result.welcome + '\n您进入了比赛模式的房间,我们推荐使用竞技卡组!';
}
return result;
};
ROOM_find_or_create_ai = function(name) {
var ainame, namea, result, room, windbot;
if (name === '') {
name = 'AI';
}
if (name.slice(0, 3) === 'AI_') {
name = 'AI#' + name.slice(3);
}
namea = name.split('#');
if (room = ROOM_find_by_name(name)) {
return room;
} else if (name === 'AI') {
windbot = _.sample(settings.modules.windbots);
name = 'AI#' + Math.floor(Math.random() * 100000);
} else if (namea.length > 1) {
ainame = namea[namea.length - 1];
windbot = _.sample(_.filter(settings.modules.windbots, function(w) {
return w.name === ainame || w.deck === ainame;
}));
if (!windbot) {
return {
"error": "未找到该AI角色或卡组"
};
}
name = name + ',' + Math.floor(Math.random() * 100000);
} else {
windbot = _.sample(settings.modules.windbots);
name = name + '#' + Math.floor(Math.random() * 100000);
}
if (name.replace(/[^\x00-\xff]/g, "00").length > 20) {
log.info("long ai name", name);
return {
"error": "AI房间名过长,请在建立房间后输入 /ai 来添加AI"
};
}
result = new Room(name);
result.windbot = windbot;
return result;
};
ROOM_find_by_name = function(name) {
var result;
result = _.find(ROOM_all, function(room) {
return room && room.name === name;
});
return result;
};
ROOM_find_by_port = function(port) {
return _.find(ROOM_all, function(room) {
return room && room.port === port;
});
};
ROOM_validate = function(name) {
var client_name, client_name_and_pass, client_pass;
client_name_and_pass = name.split('$', 2);
client_name = client_name_and_pass[0];
client_pass = client_name_and_pass[1];
if (!client_pass) {
return true;
}
return !_.find(ROOM_all, function(room) {
var room_name, room_name_and_pass, room_pass;
if (!room) {
return false;
}
room_name_and_pass = room.name.split('$', 2);
room_name = room_name_and_pass[0];
room_pass = room_name_and_pass[1];
return client_name === room_name && client_pass !== room_pass;
});
};
Room = (function() {
function Room(name, hostinfo) {
var draw_count, error1, lflist, param, rule, start_hand, start_lp, time_limit;
this.hostinfo = hostinfo;
this.name = name;
this.alive = true;
this.players = [];
this.player_datas = [];
this.status = 'starting';
this.started = false;
this.established = false;
this.watcher_buffers = [];
this.recorder_buffers = [];
this.cloud_replay_id = Math.floor(Math.random() * 100000000);
this.watchers = [];
this.random_type = '';
this.welcome = '';
ROOM_all.push(this);
this.hostinfo || (this.hostinfo = {
lflist: settings.lflist.length ? 0 : -1,
rule: settings.modules.enable_TCG_as_default ? 2 : 0,
mode: 0,
enable_priority: false,
no_check_deck: false,
no_shuffle_deck: false,
start_lp: 8000,
start_hand: 5,
draw_count: 1,
time_limit: 180,
replay_mode: settings.modules.tournament_mode.enabled ? 1 : 0
});
if (name.slice(0, 2) === 'M#') {
this.hostinfo.mode = 1;
} else if (name.slice(0, 2) === 'T#') {
this.hostinfo.mode = 2;
this.hostinfo.start_lp = 16000;
} else if ((param = name.match(/^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i))) {
this.hostinfo.rule = parseInt(param[1]);
this.hostinfo.mode = parseInt(param[2]);
this.hostinfo.enable_priority = param[3] === 'T';
this.hostinfo.no_check_deck = param[4] === 'T';
this.hostinfo.no_shuffle_deck = param[5] === 'T';
this.hostinfo.start_lp = parseInt(param[6]);
this.hostinfo.start_hand = parseInt(param[7]);
this.hostinfo.draw_count = parseInt(param[8]);
} else if ((param = name.match(/(.+)#/)) !== null) {
rule = param[1].toUpperCase();
if (rule.match(/(^|,|,)(M|MATCH)(,|,|$)/)) {
this.hostinfo.mode = 1;
}
if (rule.match(/(^|,|,)(T|TAG)(,|,|$)/)) {
this.hostinfo.mode = 2;
this.hostinfo.start_lp = 16000;
}
if (rule.match(/(^|,|,)(TCGONLY|TO)(,|,|$)/)) {
this.hostinfo.rule = 1;
this.hostinfo.lflist = _.findIndex(settings.lflist, function(list) {
return list.tcg;
});
}
if (rule.match(/(^|,|,)(OCGONLY|OO)(,|,|$)/)) {
this.hostinfo.rule = 0;
}
if (rule.match(/(^|,|,)(OT|TCG)(,|,|$)/)) {
this.hostinfo.rule = 2;
}
if ((param = rule.match(/(^|,|,)LP(\d+)(,|,|$)/))) {
start_lp = parseInt(param[2]);
if (start_lp <= 0) {
start_lp = 1;
}
if (start_lp >= 99999) {
start_lp = 99999;
}
this.hostinfo.start_lp = start_lp;
}
if ((param = rule.match(/(^|,|,)(TIME|TM|TI)(\d+)(,|,|$)/))) {
time_limit = parseInt(param[3]);
if (time_limit < 0) {
time_limit = 180;
}
if (time_limit >= 1 && time_limit <= 60) {
time_limit = time_limit * 60;
}
if (time_limit >= 999) {
time_limit = 999;
}
this.hostinfo.time_limit = time_limit;
}
if ((param = rule.match(/(^|,|,)(START|ST)(\d+)(,|,|$)/))) {
start_hand = parseInt(param[3]);
if (start_hand <= 0) {
start_hand = 1;
}
if (start_hand >= 40) {
start_hand = 40;
}
this.hostinfo.start_hand = start_hand;
}
if ((param = rule.match(/(^|,|,)(DRAW|DR)(\d+)(,|,|$)/))) {
draw_count = parseInt(param[3]);
if (draw_count >= 35) {
draw_count = 35;
}
this.hostinfo.draw_count = draw_count;
}
if ((param = rule.match(/(^|,|,)(LFLIST|LF)(\d+)(,|,|$)/))) {
lflist = parseInt(param[3]) - 1;
this.hostinfo.lflist = lflist;
}
if (rule.match(/(^|,|,)(NOLFLIST|NF)(,|,|$)/)) {
this.hostinfo.lflist = -1;
}
if (rule.match(/(^|,|,)(NOUNIQUE|NU)(,|,|$)/)) {
this.hostinfo.rule = 3;
}
if (rule.match(/(^|,|,)(NOCHECK|NC)(,|,|$)/)) {
this.hostinfo.no_check_deck = true;
}
if (rule.match(/(^|,|,)(NOSHUFFLE|NS)(,|,|$)/)) {
this.hostinfo.no_shuffle_deck = true;
}
if (rule.match(/(^|,|,)(IGPRIORITY|PR)(,|,|$)/)) {
this.hostinfo.enable_priority = true;
}
}
param = [0, this.hostinfo.lflist, this.hostinfo.rule, this.hostinfo.mode, (this.hostinfo.enable_priority ? 'T' : 'F'), (this.hostinfo.no_check_deck ? 'T' : 'F'), (this.hostinfo.no_shuffle_deck ? 'T' : 'F'), this.hostinfo.start_lp, this.hostinfo.start_hand, this.hostinfo.draw_count, this.hostinfo.time_limit, this.hostinfo.replay_mode];
try {
this.process = spawn('./ygopro', param, {
cwd: 'ygopro'
});
this.process.on('exit', (function(_this) {
return function(code) {
if (!_this.disconnector) {
_this.disconnector = 'server';
}
_this["delete"]();
};
})(this));
this.process.stdout.setEncoding('utf8');
this.process.stdout.once('data', (function(_this) {
return function(data) {
_this.established = true;
if (!_this["private"] && settings.modules.enable_websocket_roomlist) {
roomlist.create(_this);
}
_this.port = parseInt(data);
_.each(_this.players, function(player) {
player.server.connect(_this.port, '127.0.0.1', function() {
var buffer, k, len, ref;
ref = player.pre_establish_buffers;
for (k = 0, len = ref.length; k < len; k++) {
buffer = ref[k];
player.server.write(buffer);
}
player.established = true;
player.pre_establish_buffers = [];
});
});
if (_this.windbot) {
setTimeout(function() {
return _this.add_windbot(_this.windbot);
}, 200);
}
};
})(this));
this.process.stderr.on('data', (function(_this) {
return function(data) {
data = "Debug: " + data;
data = data.replace(/\n$/, "");
log.info("YGOPRO " + data);
ygopro.stoc_send_chat_to_room(_this, data, ygopro.constants.COLORS.RED);
_this.has_ygopro_error = true;
};
})(this));
} catch (error1) {
this.error = "建立房间失败,请重试";
}
}
Room.prototype["delete"] = function() {
var index, log_rep_id, player_ips, player_names, recorder_buffer, replay_id;
if (this.deleted) {
return;
}
if (this.player_datas.length && settings.modules.enable_cloud_replay) {
replay_id = this.cloud_replay_id;
if (this.has_ygopro_error) {
log_rep_id = true;
}
player_names = this.player_datas[0].name + (this.player_datas[2] ? "+" + this.player_datas[2].name : "") + " VS " + (this.player_datas[1] ? this.player_datas[1].name : "AI") + (this.player_datas[3] ? "+" + this.player_datas[3].name : "");
player_ips = [];
_.each(this.player_datas, function(player) {
player_ips.push(player.ip);
});
recorder_buffer = Buffer.concat(this.recorder_buffers);
zlib.deflate(recorder_buffer, function(err, replay_buffer) {
var date_time, recorded_ip;
replay_buffer = replay_buffer.toString('binary');
date_time = moment().format('YYYY-MM-DD HH:mm:ss');
redisdb.hmset("replay:" + replay_id, "replay_id", replay_id, "replay_buffer", replay_buffer, "player_names", player_names, "date_time", date_time);
if (!log_rep_id) {
redisdb.expire("replay:" + replay_id, 60 * 60 * 24);
}
recorded_ip = [];
_.each(player_ips, function(player_ip) {
if (_.contains(recorded_ip, player_ip)) {
return;
}
recorded_ip.push(player_ip);
redisdb.lpush(player_ip + ":replays", replay_id);
});
if (log_rep_id) {
log.info("error replay: R#" + replay_id);
}
});
}
this.watcher_buffers = [];
this.recorder_buffers = [];
this.players = [];
if (this.watcher) {
this.watcher.destroy();
}
if (this.recorder) {
this.recorder.destroy();
}
this.deleted = true;
index = _.indexOf(ROOM_all, this);
if (index !== -1) {
ROOM_all[index] = null;
}
if (!this["private"] && !this.started && this.established && settings.modules.enable_websocket_roomlist) {
roomlist["delete"](this.name);
}
};
Room.prototype.get_playing_player = function() {
var playing_player;
playing_player = [];
_.each(this.players, function(player) {
if (player.pos < 4) {
playing_player.push(player);
}
});
return playing_player;
};
Room.prototype.get_host = function() {
var host_player;
host_player = null;
_.each(this.players, function(player) {
if (player.is_host) {
host_player = player;
}
});
return host_player;
};
Room.prototype.add_windbot = function(botdata) {
this.windbot = botdata;
request({
url: "http://127.0.0.1:" + settings.modules.windbot_port + "/?name=" + (encodeURIComponent(botdata.name)) + "&deck=" + (encodeURIComponent(botdata.deck)) + "&host=127.0.0.1&port=" + settings.port + "&dialog=" + (encodeURIComponent(botdata.dialog)) + "&version=" + settings.version + "&password=" + (encodeURIComponent(this.name))
}, (function(_this) {
return function(error, response, body) {
if (error) {
log.warn('windbot add error', error, _this.name, response);
ygopro.stoc_send_chat_to_room(_this, "添加AI失败,可尝试输入 /ai 重新添加", ygopro.constants.COLORS.RED);
}
};
})(this));
};
Room.prototype.connect = function(client) {
var host_player;
this.players.push(client);
if (this.random_type) {
client.abuse_count = 0;
host_player = this.get_host();
if (host_player && (host_player !== client)) {
ROOM_players_oppentlist[host_player.ip] = client.ip;
ROOM_players_oppentlist[client.ip] = host_player.ip;
} else {
ROOM_players_oppentlist[client.ip] = null;
}
}
if (this.established) {
if (!this["private"] && !this.started && settings.modules.enable_websocket_roomlist) {
roomlist.update(this);
}
client.server.connect(this.port, '127.0.0.1', function() {
var buffer, k, len, ref;
ref = client.pre_establish_buffers;
for (k = 0, len = ref.length; k < len; k++) {
buffer = ref[k];
client.server.write(buffer);
}
client.established = true;
client.pre_establish_buffers = [];
});
}
};
Room.prototype.disconnect = function(client, error) {
var index;
if (client.is_post_watcher) {
ygopro.stoc_send_chat_to_room(this, (client.name + " 退出了观战") + (error ? ": " + error : ''));
index = _.indexOf(this.watchers, client);
if (index !== -1) {
this.watchers.splice(index, 1);
}
} else {
index = _.indexOf(this.players, client);
if (index !== -1) {
this.players.splice(index, 1);
}
if (this.started && this.disconnector !== 'server' && this.random_type && (client.pos < 4 || client.is_host)) {
ROOM_ban_player(client.name, client.ip, "强退");
}
if (this.players.length && !(this.windbot && client.is_host)) {
ygopro.stoc_send_chat_to_room(this, (client.name + " 离开了游戏") + (error ? ": " + error : ''));
if (!this["private"] && !this.started && settings.modules.enable_websocket_roomlist) {
roomlist.update(this);
}
} else {
this.process.kill();
this["delete"]();
}
}
};
return Room;
})();
net.createServer(function(client) {
var ctos_buffer, ctos_message_length, ctos_proto, server, stoc_buffer, stoc_message_length, stoc_proto;
var connect_count, server;
client.ip = client.remoteAddress;
connect_count = ROOM_connected_ip[client.ip] || 0;
if (client.ip !== '::ffff:127.0.0.1') {
connect_count++;
}
ROOM_connected_ip[client.ip] = connect_count;
server = new net.Socket();
client.server = server;
client.setTimeout(300000);
client.setTimeout(2000);
client.on('close', function(had_error) {
var room;
room = ROOM_all[client.rid];
connect_count = ROOM_connected_ip[client.ip];
if (connect_count > 0) {
connect_count--;
}
ROOM_connected_ip[client.ip] = connect_count;
tribute(client);
if (!client.closed) {
client.closed = true;
if (client.room) {
client.room.disconnect(client);
if (room) {
room.disconnect(client);
}
}
server.end();
server.destroy();
});
client.on('error', function(error) {
var room;
room = ROOM_all[client.rid];
connect_count = ROOM_connected_ip[client.ip];
if (connect_count > 0) {
connect_count--;
}
ROOM_connected_ip[client.ip] = connect_count;
tribute(client);
if (!client.closed) {
client.closed = error;
if (client.room) {
client.room.disconnect(client, error);
if (room) {
room.disconnect(client, error);
}
}
server.end();
server.destroy();
});
client.on('timeout', function() {
server.end();
server.destroy();
});
server.on('close', function(had_error) {
var room;
room = ROOM_all[client.rid];
tribute(server);
client.room.disconnector = 'server';
if (room) {
room.disconnector = 'server';
}
if (!server.closed) {
server.closed = true;
}
if (!client.closed) {
ygopro.stoc_send_chat(client, "服务器关闭了连接", 11);
client.end();
ygopro.stoc_send_chat(client, "服务器关闭了连接", ygopro.constants.COLORS.RED);
client.destroy();
}
});
server.on('error', function(error) {
var room;
room = ROOM_all[client.rid];
tribute(server);
client.room.disconnector = 'server';
if (room) {
room.disconnector = 'server';
}
server.closed = error;
if (!client.closed) {
ygopro.stoc_send_chat(client, "服务器错误: " + error, 11);
client.end();
ygopro.stoc_send_chat(client, "服务器错误: " + error, ygopro.constants.COLORS.RED);
client.destroy();
}
});
/*
client.open_cloud_replay= (err, replay)->
if err or !replay
ygopro.stoc_send_chat(client,"没有找到录像", 11)
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 2
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
client.destroy();
return;
}
if (settings.modules.enable_cloud_replay) {
client.open_cloud_replay = function(err, replay) {
var buffer;
if (err || !replay) {
ygopro.stoc_die(client, "没有找到录像");
return;
}
redisdb.expire("replay:" + replay.replay_id, 60 * 60 * 48);
buffer = new Buffer(replay.replay_buffer, 'binary');
zlib.unzip(buffer, function(err, replay_buffer) {
if (err) {
log.info("cloud replay unzip error: " + err);
ygopro.stoc_send_chat(client, "播放录像出错", ygopro.constants.COLORS.RED);
client.destroy();
return;
}
ygopro.stoc_send_chat(client, "正在观看云录像:R#" + replay.replay_id + " " + replay.player_names + " " + replay.date_time, ygopro.constants.COLORS.BABYBLUE);
client.write(replay_buffer);
setTimeout((function() {
client.destroy();
}), 5000);
});
};
}
client.end()
return
replay_buffer=new Buffer(replay.replay_buffer,'binary')
ygopro.stoc_send_chat(client,"正在观看云录像:R##{replay.replay_id} #{replay.player_names} #{replay.date_time}", 14)
client.write replay_buffer
client.end()
return
*/
ctos_buffer = new Buffer(0);
ctos_message_length = 0;
ctos_proto = 0;
client.pre_establish_buffers = new Array();
client.on('data', function(data) {
var b, buffer, cancel, datas, k, l, len, len1, looplimit, struct;
client.on('data', function(ctos_buffer) {
var b, bad_ip_count, buffer, cancel, ctos_message_length, ctos_proto, datas, k, l, len, len1, looplimit, room, struct;
if (client.is_post_watcher) {
client.room.watcher.write(data);
room = ROOM_all[client.rid];
if (room) {
room.watcher.write(ctos_buffer);
}
} else {
ctos_buffer = Buffer.concat([ctos_buffer, data], ctos_buffer.length + data.length);
ctos_message_length = 0;
ctos_proto = 0;
datas = [];
looplimit = 0;
while (true) {
......@@ -193,12 +838,16 @@
if (ctos_buffer.length >= 2) {
ctos_message_length = ctos_buffer.readUInt16LE(0);
} else {
if (ctos_buffer.length !== 0) {
log.warn("bad ctos_buffer length", client.ip);
}
break;
}
} else if (ctos_proto === 0) {
if (ctos_buffer.length >= 3) {
ctos_proto = ctos_buffer.readUInt8(2);
} else {
log.warn("bad ctos_proto length", client.ip);
break;
}
} else {
......@@ -224,13 +873,22 @@
ctos_message_length = 0;
ctos_proto = 0;
} else {
if (ctos_message_length !== 17735) {
log.warn("bad ctos_message length", client.ip, ctos_buffer.length, ctos_message_length, ctos_proto);
}
break;
}
}
looplimit++;
if (looplimit > 800) {
log.info("error ctos", client.name);
server.end();
if (looplimit > 800 || ROOM_bad_ip[client.ip] > 5) {
log.info("error ctos", client.name, client.ip);
bad_ip_count = ROOM_bad_ip[client.ip];
if (bad_ip_count) {
ROOM_bad_ip[client.ip] = bad_ip_count + 1;
} else {
ROOM_bad_ip[client.ip] = 1;
}
client.destroy();
break;
}
}
......@@ -247,53 +905,72 @@
}
}
});
stoc_buffer = new Buffer(0);
server.on('data', function(stoc_buffer) {
var b, buffer, cancel, datas, k, len, looplimit, stanzas, stoc_message_length, stoc_proto, struct;
stoc_message_length = 0;
stoc_proto = 0;
server.on('data', function(data) {
var b, looplimit, stanzas, struct;
stoc_buffer = Buffer.concat([stoc_buffer, data], stoc_buffer.length + data.length);
client.write(data);
datas = [];
looplimit = 0;
while (true) {
if (stoc_message_length === 0) {
if (stoc_buffer.length >= 2) {
stoc_message_length = stoc_buffer.readUInt16LE(0);
} else {
if (stoc_buffer.length !== 0) {
log.warn("bad stoc_buffer length", client.ip);
}
break;
}
} else if (stoc_proto === 0) {
if (stoc_buffer.length >= 3) {
stoc_proto = stoc_buffer.readUInt8(2);
} else {
log.warn("bad stoc_proto length", client.ip);
break;
}
} else {
if (stoc_buffer.length >= 2 + stoc_message_length) {
cancel = false;
stanzas = stoc_proto;
if (ygopro.stoc_follows[stoc_proto]) {
b = stoc_buffer.slice(3, stoc_message_length - 1 + 3);
if (struct = ygopro.structs[ygopro.proto_structs.STOC[ygopro.constants.STOC[stoc_proto]]]) {
struct._setBuff(b);
if (ygopro.stoc_follows[stoc_proto].synchronous) {
cancel = ygopro.stoc_follows[stoc_proto].callback(b, _.clone(struct.fields), client, server);
} else {
ygopro.stoc_follows[stoc_proto].callback(b, _.clone(struct.fields), client, server);
}
} else {
if (ygopro.stoc_follows[stoc_proto].synchronous) {
cancel = ygopro.stoc_follows[stoc_proto].callback(b, null, client, server);
} else {
ygopro.stoc_follows[stoc_proto].callback(b, null, client, server);
}
}
}
if (!cancel) {
datas.push(stoc_buffer.slice(0, 2 + stoc_message_length));
}
stoc_buffer = stoc_buffer.slice(2 + stoc_message_length);
stoc_message_length = 0;
stoc_proto = 0;
} else {
log.warn("bad stoc_message length", client.ip);
break;
}
}
looplimit++;
if (looplimit > 800) {
log.info("error stoc", client.name);
server.end();
server.destroy();
break;
}
}
for (k = 0, len = datas.length; k < len; k++) {
buffer = datas[k];
client.write(buffer);
}
});
}).listen(settings.port, function() {
log.info("server started", settings.port);
......@@ -311,105 +988,70 @@
});
ygopro.ctos_follow('JOIN_GAME', false, function(buffer, info, client, server) {
var check, decrypted_buffer, finish, i, id, k, l, len, len1, name, ref, ref1, room, secret, windbot;
var check, decrypted_buffer, finish, i, id, k, l, len, len1, name, ref, ref1, replay_id, room, secret, struct;
if (settings.modules.stop) {
ygopro.stoc_send_chat(client, settings.modules.stop, 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
/*
else if info.pass.toUpperCase()=="R"
ygopro.stoc_send_chat(client,"以下是您近期的云录像,密码处输入 R#录像编号 即可观看", 14)
redisdb.lrange client.remoteAddress+":replays", 0, 2, (err, result)=>
_.each result, (replay_id,id)=>
redisdb.hgetall "replay:"+replay_id, (err, replay)=>
ygopro.stoc_send_chat(client,"<#{id-0+1}> R##{replay_id} #{replay.player_names} #{replay.date_time}", 14)
return
return
return
#强行等待异步执行完毕_(:з」∠)_
setTimeout (()=>
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 2
ygopro.stoc_die(client, settings.modules.stop);
} else if (info.pass.toUpperCase() === "R" && settings.modules.enable_cloud_replay) {
ygopro.stoc_send_chat(client, "以下是您近期的云录像,密码处输入 R#录像编号 即可观看", ygopro.constants.COLORS.BABYBLUE);
redisdb.lrange(client.ip + ":replays", 0, 2, function(err, result) {
_.each(result, function(replay_id, id) {
redisdb.hgetall("replay:" + replay_id, function(err, replay) {
if (err || !replay) {
if (err) {
log.info("cloud replay getall error: " + err);
}
client.end()), 500
else if info.pass[0...2].toUpperCase()=="R#"
replay_id=info.pass.split("#")[1]
if (replay_id>0 and replay_id<=3)
redisdb.lindex client.remoteAddress+":replays", replay_id-1, (err, replay_id)=>
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
return
else if replay_id
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
else
ygopro.stoc_send_chat(client,"没有找到录像", 11)
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 2
return;
}
client.end()
*/
} else if (info.version !== settings.version) {
ygopro.stoc_send_chat(client, settings.modules.update, 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 4,
code: settings.version
ygopro.stoc_send_chat(client, "<" + (id - 0 + 1) + "> R#" + replay_id + " " + replay.player_names + " " + replay.date_time, ygopro.constants.COLORS.BABYBLUE);
});
client.end();
} else if (!info.pass.length && !settings.modules.enable_random_duel) {
ygopro.stoc_send_chat(client, "房间名为空,请填写主机密码", 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
} else if (settings.modules.windbot && info.pass.slice(0, 2) === 'AI') {
if (info.pass.length > 3 && info.pass.slice(0, 3) === 'AI#' || info.pass.slice(0, 3) === 'AI_') {
name = info.pass.slice(3);
windbot = _.sample(_.filter(settings.modules.windbot, function(w) {
return w.name === name || w.deck === name;
}));
if (!windbot) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Windbot Name)', 11);
});
setTimeout((function() {
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
client.destroy();
}), 500);
} else if (info.pass.slice(0, 2).toUpperCase() === "R#" && settings.modules.enable_cloud_replay) {
replay_id = info.pass.split("#")[1];
if (replay_id > 0 && replay_id <= 9) {
redisdb.lindex(client.ip + ":replays", replay_id - 1, function(err, replay_id) {
if (err || !replay_id) {
if (err) {
log.info("cloud replay replayid error: " + err);
}
ygopro.stoc_die(client, "没有找到录像");
return;
}
redisdb.hgetall("replay:" + replay_id, client.open_cloud_replay);
});
} else if (replay_id) {
redisdb.hgetall("replay:" + replay_id, client.open_cloud_replay);
} else {
windbot = _.sample(settings.modules.windbot);
ygopro.stoc_die(client, "没有找到录像");
}
room = Room.find_or_create_by_name('AI#' + Math.random().toString());
room.windbot = windbot;
room["private"] = true;
client.room = room;
client.room.connect(client);
} else if (info.pass.length && settings.modules.mycard_auth) {
ygopro.stoc_send_chat(client, '正在读取用户信息...', 11);
if (info.pass.length <= 8) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Length)', 11);
} else if (info.pass.toUpperCase() === "W" && settings.modules.enable_cloud_replay) {
replay_id = Cloud_replay_ids[Math.floor(Math.random() * Cloud_replay_ids.length)];
redisdb.hgetall("replay:" + replay_id, client.open_cloud_replay);
} else if (info.version !== settings.version && (info.version !== 4921 || settings.version !== 4922)) {
ygopro.stoc_send_chat(client, settings.modules.update, ygopro.constants.COLORS.RED);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
msg: 4,
code: settings.version
});
client.end();
client.destroy();
} else if (!info.pass.length && !settings.modules.enable_random_duel && !settings.modules.enable_windbot) {
ygopro.stoc_die(client, "房间名为空,请在主机密码处填写房间名");
} else if (info.pass.length && settings.modules.mycard_auth && info.pass.slice(0, 2) !== 'AI') {
ygopro.stoc_send_chat(client, '正在读取用户信息...', ygopro.constants.COLORS.BABYBLUE);
if (info.pass.length <= 8) {
ygopro.stoc_die(client, '主机密码不正确 (Invalid Length)');
return;
}
buffer = new Buffer(info.pass.slice(0, 8), 'base64');
if (buffer.length !== 6) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Payload Length)', 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, '主机密码不正确 (Invalid Payload Length)');
return;
}
check = function(buf) {
......@@ -421,28 +1063,18 @@
return (checksum & 0xFF) === 0;
};
finish = function(buffer) {
var action, opt1, opt2, opt3, options;
var action, name, opt1, opt2, opt3, options, room;
action = buffer.readUInt8(1) >> 4;
if (buffer !== decrypted_buffer && (action === 1 || action === 2 || action === 4)) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Unauthorized)', 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, '主机密码不正确 (Unauthorized)');
return;
}
switch (action) {
case 1:
case 2:
name = crypto.createHash('md5').update(info.pass + client.name).digest('base64').slice(0, 10).replace('+', '-').replace('/', '_');
if (Room.find_by_name(name)) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Already Existed)', 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
if (ROOM_find_by_name(name)) {
ygopro.stoc_die(client, '主机密码不正确 (Already Existed)');
return;
}
opt1 = buffer.readUInt8(2);
......@@ -469,32 +1101,29 @@
break;
case 3:
name = info.pass.slice(8);
room = Room.find_by_name(name);
room = ROOM_find_by_name(name);
if (!room) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Not Found)', 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, '主机密码不正确 (Not Found)');
return;
}
break;
case 4:
room = Room.find_or_create_by_name('M#' + info.pass.slice(8));
room = ROOM_find_or_create_by_name('M#' + info.pass.slice(8));
room["private"] = true;
break;
default:
ygopro.stoc_send_chat(client, '主机密码不正确 (Invalid Action)', 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, '主机密码不正确 (Invalid Action)');
return;
}
client.room = room;
return client.room.connect(client);
if (!room) {
ygopro.stoc_die(client, "服务器已经爆满,请稍候再试");
} else if (room.error) {
ygopro.stoc_die(client, room.error);
} else {
client.setTimeout(300000);
client.rid = _.indexOf(ROOM_all, room);
room.connect(client);
}
};
if (id = users_cache[client.name]) {
secret = id % 65535 + 1;
......@@ -512,7 +1141,7 @@
baseUrl: settings.modules.mycard_auth,
url: '/users/' + encodeURIComponent(client.name) + '.json',
qs: {
api_key: 'dc7298a754828b3d26b709f035a0eeceb43e73cbd8c4fa8dec18951f8a95d2bc',
api_key: process.env.MYCARD_AUTH_KEY,
api_username: client.name,
skip_track_visit: true
},
......@@ -532,103 +1161,120 @@
}
}
if (!check(buffer)) {
ygopro.stoc_send_chat(client, '主机密码不正确 (Checksum Failed)', 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, '主机密码不正确 (Checksum Failed)');
return;
}
users_cache[client.name] = body.user.id;
return finish(buffer);
});
} else if (info.pass.length && !Room.validate(info.pass)) {
ygopro.stoc_send_chat(client, "房间密码不正确", 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
} else if (client.name === '[INCORRECT]') {
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
} else if (_.indexOf(settings.BANNED_user, client.name) > -1) {
settings.BANNED_IP.push(client.remoteAddress);
log.info("BANNED USER LOGIN", client.name, client.remoteAddress);
ygopro.stoc_send_chat(client, "您的账号已被封禁", 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
} else if (_.indexOf(settings.BANNED_IP, client.remoteAddress) > -1) {
log.info("BANNED IP LOGIN", client.name, client.remoteAddress);
ygopro.stoc_send_chat(client, "您的账号已被封禁", 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
} else if (!client.name || client.name === "") {
ygopro.stoc_die(client, "请输入正确的用户名");
} else if (ROOM_connected_ip[client.ip] > 5) {
log.warn("MULTI LOGIN", client.name, client.ip);
ygopro.stoc_die(client, "同时开启的客户端数量过多 " + client.ip);
} else if (_.indexOf(settings.ban.banned_user, client.name) > -1) {
settings.ban.banned_ip.push(client.ip);
log.warn("BANNED USER LOGIN", client.name, client.ip);
ygopro.stoc_die(client, "您的账号已被封禁");
} else if (_.indexOf(settings.ban.banned_ip, client.ip) > -1) {
log.warn("BANNED IP LOGIN", client.name, client.ip);
ygopro.stoc_die(client, "您的账号已被封禁");
} else if (_.any(settings.ban.badword_level3, function(badword) {
var regexp;
regexp = new RegExp(badword, 'i');
return name.match(regexp);
}, name = client.name)) {
log.warn("BAD NAME LEVEL 3", client.name, client.ip);
ygopro.stoc_die(client, "您的用户名存在不适当的内容");
} else if (_.any(settings.ban.badword_level2, function(badword) {
var regexp;
regexp = new RegExp(badword, 'i');
return name.match(regexp);
}, name = client.name)) {
log.warn("BAD NAME LEVEL 2", client.name, client.ip);
ygopro.stoc_die(client, "您的用户名存在不适当的内容");
} else if (_.any(settings.ban.badword_level1, function(badword) {
var regexp;
regexp = new RegExp(badword, 'i');
return name.match(regexp);
}, name = client.name)) {
log.warn("BAD NAME LEVEL 1", client.name, client.ip);
ygopro.stoc_die(client, "您的用户名存在不适当的内容,请注意更改");
} else if (info.pass.length && !ROOM_validate(info.pass)) {
ygopro.stoc_die(client, "房间密码不正确");
} else {
room = Room.find_or_create_by_name(info.pass, client.remoteAddress);
if (info.version === 4921 && settings.version === 4922) {
info.version = settings.version;
struct = ygopro.structs["CTOS_JoinGame"];
struct._setBuff(buffer);
struct.set("version", info.version);
buffer = struct.buffer;
}
room = ROOM_find_or_create_by_name(info.pass, client.ip);
if (!room) {
ygopro.stoc_send_chat(client, "服务器已经爆满,请稍候再试", 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, "服务器已经爆满,请稍候再试");
} else if (room.error) {
ygopro.stoc_send_chat(client, room.error, 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, room.error);
} else if (room.started) {
if (settings.modules.post_start_watching) {
client.room = room;
if (settings.modules.enable_halfway_watch) {
client.setTimeout(300000);
client.rid = _.indexOf(ROOM_all, room);
client.is_post_watcher = true;
ygopro.stoc_send_chat_to_room(client.room, client.name + " 加入了观战");
client.room.watchers.push(client);
ygopro.stoc_send_chat(client, "观战中", 14);
ref1 = client.room.watcher_buffers;
ygopro.stoc_send_chat_to_room(room, client.name + " 加入了观战");
room.watchers.push(client);
ygopro.stoc_send_chat(client, "观战中", ygopro.constants.COLORS.BABYBLUE);
ref1 = room.watcher_buffers;
for (l = 0, len1 = ref1.length; l < len1; l++) {
buffer = ref1[l];
client.write(buffer);
}
} else {
ygopro.stoc_send_chat(client, "决斗已开始,不允许观战", 11);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
client.end();
ygopro.stoc_die(client, "决斗已开始,不允许观战");
}
} else {
client.room = room;
client.room.connect(client);
client.setTimeout(300000);
client.rid = _.indexOf(ROOM_all, room);
room.connect(client);
}
}
});
ygopro.stoc_follow('JOIN_GAME', false, function(buffer, info, client, server) {
var watcher;
if (!client.room) {
var recorder, room, watcher;
room = ROOM_all[client.rid];
if (!room) {
return;
}
if (settings.modules.welcome) {
ygopro.stoc_send_chat(client, settings.modules.welcome);
ygopro.stoc_send_chat(client, settings.modules.welcome, ygopro.constants.COLORS.GREEN);
}
if (room.welcome) {
ygopro.stoc_send_chat(client, room.welcome, ygopro.constants.COLORS.BABYBLUE);
}
if (!room.recorder) {
room.recorder = recorder = net.connect(room.port, function() {
ygopro.ctos_send(recorder, 'PLAYER_INFO', {
name: "Marshtomp"
});
ygopro.ctos_send(recorder, 'JOIN_GAME', {
version: settings.version,
gameid: 2577,
some_unknown_mysterious_fucking_thing: 0,
pass: ""
});
ygopro.ctos_send(recorder, 'HS_TOOBSERVER');
});
recorder.on('data', function(data) {
room = ROOM_all[client.rid];
if (!(room && settings.modules.enable_cloud_replay)) {
return;
}
if (client.room.welcome) {
ygopro.stoc_send_chat(client, client.room.welcome, 14);
room.recorder_buffers.push(data);
});
recorder.on('error', function(error) {});
}
if (settings.modules.post_start_watching && !client.room.watcher) {
client.room.watcher = watcher = net.connect(client.room.port, function() {
if (settings.modules.enable_halfway_watch && !room.watcher) {
room.watcher = watcher = net.connect(room.port, function() {
ygopro.ctos_send(watcher, 'PLAYER_INFO', {
name: "the Big Brother"
});
......@@ -642,11 +1288,12 @@
});
watcher.on('data', function(data) {
var k, len, ref, w;
if (!client.room) {
room = ROOM_all[client.rid];
if (!room) {
return;
}
client.room.watcher_buffers.push(data);
ref = client.room.watchers;
room.watcher_buffers.push(data);
ref = room.watchers;
for (k = 0, len = ref.length; k < len; k++) {
w = ref[k];
if (w) {
......@@ -658,8 +1305,7 @@
}
});
if (settings.modules.dialogues) {
dialogues = {};
load_dialogues = function() {
request({
url: settings.modules.dialogues,
json: true
......@@ -669,41 +1315,49 @@
} else if (error || !body) {
log.warn('dialogues error', error, response);
} else {
dialogues = body;
nconf.myset(settings, "dialogues", body);
log.info("dialogues loaded", _.size(body));
}
});
};
if (settings.modules.dialogues) {
load_dialogues();
}
ygopro.stoc_follow('GAME_MSG', false, function(buffer, info, client, server) {
var card, k, len, line, msg, playertype, pos, ref, ref1, ref2, val;
var card, k, len, line, msg, playertype, pos, reason, ref, ref1, ref2, room, val;
room = ROOM_all[client.rid];
if (!room) {
return;
}
msg = buffer.readInt8(0);
if (msg >= 10 && msg < 30) {
client.room.waiting_for_player = client;
client.room.last_active_time = moment();
room.waiting_for_player = client;
room.last_active_time = moment();
}
if (ygopro.constants.MSG[msg] === 'START') {
playertype = buffer.readUInt8(1);
client.is_first = !(playertype & 0xf);
client.lp = client.room.hostinfo.start_lp;
client.lp = room.hostinfo.start_lp;
}
if (ygopro.constants.MSG[msg] === 'WIN' && client.is_host) {
pos = buffer.readUInt8(1);
if (!(client.is_first || pos === 2)) {
pos = 1 - pos;
}
reason = buffer.readUInt8(2);
room.winner = pos;
}
/*
if ygopro.constants.MSG[msg] == 'WIN' and _.startsWith(client.room.name, 'M#') and client.is_host
pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first or pos == 2
reason = buffer.readUInt8(2)
#log.info {winner: pos, reason: reason}
client.room.duels.push {winner: pos, reason: reason}
*/
if (ygopro.constants.MSG[msg] === 'DAMAGE' && client.is_host) {
pos = buffer.readUInt8(1);
if (!client.is_first) {
pos = 1 - pos;
}
val = buffer.readInt32LE(2);
client.room.dueling_players[pos].lp -= val;
if ((0 < (ref = client.room.dueling_players[pos].lp) && ref <= 100)) {
ygopro.stoc_send_chat_to_room(client.room, "你的生命已经如风中残烛了!", 15);
room.dueling_players[pos].lp -= val;
if ((0 < (ref = room.dueling_players[pos].lp) && ref <= 100)) {
ygopro.stoc_send_chat_to_room(room, "你的生命已经如风中残烛了!", ygopro.constants.COLORS.PINK);
}
}
if (ygopro.constants.MSG[msg] === 'RECOVER' && client.is_host) {
......@@ -712,7 +1366,7 @@
pos = 1 - pos;
}
val = buffer.readInt32LE(2);
client.room.dueling_players[pos].lp += val;
room.dueling_players[pos].lp += val;
}
if (ygopro.constants.MSG[msg] === 'LPUPDATE' && client.is_host) {
pos = buffer.readUInt8(1);
......@@ -720,7 +1374,7 @@
pos = 1 - pos;
}
val = buffer.readInt32LE(2);
client.room.dueling_players[pos].lp = val;
room.dueling_players[pos].lp = val;
}
if (ygopro.constants.MSG[msg] === 'PAY_LPCOST' && client.is_host) {
pos = buffer.readUInt8(1);
......@@ -728,19 +1382,19 @@
pos = 1 - pos;
}
val = buffer.readInt32LE(2);
client.room.dueling_players[pos].lp -= val;
if ((0 < (ref1 = client.room.dueling_players[pos].lp) && ref1 <= 100)) {
ygopro.stoc_send_chat_to_room(client.room, "背水一战!", 15);
room.dueling_players[pos].lp -= val;
if ((0 < (ref1 = room.dueling_players[pos].lp) && ref1 <= 100)) {
ygopro.stoc_send_chat_to_room(room, "背水一战!", ygopro.constants.COLORS.PINK);
}
}
if (settings.modules.dialogues) {
if (ygopro.constants.MSG[msg] === 'SUMMONING' || ygopro.constants.MSG[msg] === 'SPSUMMONING') {
card = buffer.readUInt32LE(1);
if (dialogues[card]) {
ref2 = _.lines(dialogues[card][Math.floor(Math.random() * dialogues[card].length)]);
if (settings.dialogues[card]) {
ref2 = _.lines(settings.dialogues[card][Math.floor(Math.random() * settings.dialogues[card].length)]);
for (k = 0, len = ref2.length; k < len; k++) {
line = ref2[k];
ygopro.stoc_send_chat(client, line, 15);
ygopro.stoc_send_chat(client, line, ygopro.constants.COLORS.PINK);
}
}
}
......@@ -748,15 +1402,23 @@
});
ygopro.ctos_follow('HS_KICK', true, function(buffer, info, client, server) {
var k, len, player, ref;
if (!client.room) {
var k, len, player, ref, room;
room = ROOM_all[client.rid];
if (!room) {
return;
}
ref = client.room.players;
ref = room.players;
for (k = 0, len = ref.length; k < len; k++) {
player = ref[k];
if (player && player.pos === info.pos && player !== client) {
ygopro.stoc_send_chat_to_room(client.room, player.name + " 被请出了房间", 11);
client.kick_count = client.kick_count ? client.kick_count + 1 : 1;
if (client.kick_count >= 5) {
ygopro.stoc_send_chat_to_room(room, client.name + " 被系统请出了房间", ygopro.constants.COLORS.RED);
ROOM_ban_player(player.name, player.ip, "挂房间");
client.destroy();
return true;
}
ygopro.stoc_send_chat_to_room(room, player.name + " 被请出了房间", ygopro.constants.COLORS.RED);
}
}
return false;
......@@ -771,27 +1433,28 @@
});
ygopro.stoc_follow('HS_PLAYER_CHANGE', false, function(buffer, info, client, server) {
var is_ready, k, len, player, pos, ref;
if (!(client.room && client.room.max_player && client.is_host)) {
var is_ready, k, len, player, pos, ref, room;
room = ROOM_all[client.rid];
if (!(room && room.max_player && client.is_host)) {
return;
}
pos = info.status >> 4;
is_ready = (info.status & 0xf) === 9;
if (pos < client.room.max_player) {
client.room.ready_player_count_without_host = 0;
ref = client.room.players;
if (pos < room.max_player) {
room.ready_player_count_without_host = 0;
ref = room.players;
for (k = 0, len = ref.length; k < len; k++) {
player = ref[k];
if (player.pos === pos) {
player.is_ready = is_ready;
}
if (!player.is_host) {
client.room.ready_player_count_without_host += player.is_ready;
room.ready_player_count_without_host += player.is_ready;
}
}
if (client.room.ready_player_count_without_host >= client.room.max_player - 1) {
if (room.ready_player_count_without_host >= room.max_player - 1) {
setTimeout((function() {
wait_room_start(client.room, 20);
wait_room_start(ROOM_all[client.rid], 20);
}), 1000);
}
}
......@@ -803,7 +1466,7 @@
time -= 1;
if (time) {
if (!(time % 5)) {
ygopro.stoc_send_chat_to_room(room, "" + (time <= 9 ? ' ' : '') + time + "秒后房主若不开始游戏将被请出房间", time <= 9 ? 11 : 8);
ygopro.stoc_send_chat_to_room(room, "" + (time <= 9 ? ' ' : '') + time + "秒后房主若不开始游戏将被请出房间", time <= 9 ? ygopro.constants.COLORS.RED : ygopro.constants.COLORS.LIGHTBLUE);
}
setTimeout((function() {
wait_room_start(room, time);
......@@ -813,9 +1476,9 @@
for (k = 0, len = ref.length; k < len; k++) {
player = ref[k];
if (player && player.is_host) {
Room.ban_player(player.name, player.ip, "挂房间");
ygopro.stoc_send_chat_to_room(room, player.name + " 被系统请出了房间", 11);
player.end();
ROOM_ban_player(player.name, player.ip, "挂房间");
ygopro.stoc_send_chat_to_room(room, player.name + " 被系统请出了房间", ygopro.constants.COLORS.RED);
player.destroy();
}
}
}
......@@ -823,37 +1486,46 @@
};
ygopro.stoc_send_random_tip = function(client) {
if (tips) {
ygopro.stoc_send_chat(client, "Tip: " + tips[Math.floor(Math.random() * tips.length)]);
if (settings.modules.tips) {
ygopro.stoc_send_chat(client, "Tip: " + settings.tips[Math.floor(Math.random() * settings.tips.length)]);
}
};
ygopro.stoc_send_random_tip_to_room = function(room) {
if (tips) {
ygopro.stoc_send_chat_to_room(room, "Tip: " + tips[Math.floor(Math.random() * tips.length)]);
if (settings.modules.tips) {
ygopro.stoc_send_chat_to_room(room, "Tip: " + settings.tips[Math.floor(Math.random() * settings.tips.length)]);
}
};
load_tips = function() {
request({
url: settings.modules.tips,
json: true
}, function(error, response, body) {
if (_.isString(body)) {
log.warn("tips bad json", body);
} else if (error || !body) {
log.warn('tips error', error, response);
} else {
nconf.myset(settings, "tips", body);
log.info("tips loaded", settings.tips.length);
}
});
};
if (settings.modules.tips) {
load_tips();
setInterval(function() {
var k, len, ref, room;
ref = Room.all;
for (k = 0, len = ref.length; k < len; k++) {
room = ref[k];
var k, len, room;
for (k = 0, len = ROOM_all.length; k < len; k++) {
room = ROOM_all[k];
if (room && room.established) {
if (!(room && room.started)) {
ygopro.stoc_send_random_tip_to_room(room);
}
}
}
}, 30000);
tips = null;
if (settings.modules.tips) {
request({
url: settings.modules.tips,
json: true
}, function(error, response, body) {
tips = body;
});
}
if (settings.modules.mycard_auth && process.env.MYCARD_AUTH_DATABASE) {
......@@ -862,7 +1534,7 @@
if (error) {
throw error;
}
return client.query('SELECT username, id from users', function(error, result) {
client.query('SELECT username, id from users', function(error, result) {
var k, len, ref, row;
if (error) {
throw error;
......@@ -873,69 +1545,71 @@
row = ref[k];
users_cache[row.username] = row.id;
}
return console.log("users loaded", _.keys(users_cache).length);
console.log("users loaded", _.keys(users_cache).length);
});
});
}
ygopro.stoc_follow('DUEL_START', false, function(buffer, info, client, server) {
var k, len, player, ref;
if (!client.room) {
var deck_name, deck_text, k, len, player, ref, room;
room = ROOM_all[client.rid];
if (!room) {
return;
}
if (!client.room.started) {
client.room.started = true;
if (settings.modules.enable_websocket_roomlist && !client.room["private"]) {
roomlist["delete"](client.room.name);
if (!room.started) {
room.started = true;
if (settings.modules.enable_websocket_roomlist && !room["private"]) {
roomlist["delete"](room.name);
}
client.room.dueling_players = [];
ref = client.room.players;
room.dueling_players = [];
ref = room.players;
for (k = 0, len = ref.length; k < len; k++) {
player = ref[k];
if (!(player.pos !== 7)) {
continue;
}
client.room.dueling_players[player.pos] = player;
client.room.player_datas.push({
ip: player.remoteAddress,
room.dueling_players[player.pos] = player;
room.player_datas.push({
ip: player.ip,
name: player.name
});
if (client.room.windbot) {
client.room.dueling_players[1 - player.pos] = {};
}
}
}
if (settings.modules.tips) {
ygopro.stoc_send_random_tip(client);
}
if (settings.modules.enable_deck_log && client.main && client.main.length && !client.deck_saved) {
deck_text = '#ygosrv233 deck log\r\n#main\r\n' + client.main.join('\r\n') + '\r\n!side\r\n' + client.side.join('\r\n') + '\r\n';
deck_name = moment().format('YYYY-MM-DD HH-mm-ss') + ' ' + room.port + ' ' + client.pos + ' ' + client.name.replace(/\//g, '_');
fs.writeFile('decks_save\/' + deck_name + '.ydk', deck_text, 'utf-8', function(err) {
if (err) {
return log.warn('DECK SAVE ERROR', err);
}
});
client.deck_saved = true;
}
});
ygopro.ctos_follow('CHAT', true, function(buffer, info, client, server) {
var cancel;
cancel = _.startsWith(_.trim(info.msg), "/");
if (!(cancel || !client.room.random_type)) {
client.room.last_active_time = moment();
}
switch (_.trim(info.msg)) {
case '/ping':
execFile('ss', ['-it', "dst " + client.remoteAddress + ":" + client.remotePort], function(error, stdout, stderr) {
var line;
if (error) {
ygopro.stoc_send_chat_to_room(client.room, error);
} else {
line = _.lines(stdout)[2];
if (line.indexOf('rtt') !== -1) {
ygopro.stoc_send_chat_to_room(client.room, line);
} else {
ygopro.stoc_send_chat_to_room(client.room, stdout);
var cancel, cmd, msg, name, oldmsg, room, struct, windbot;
room = ROOM_all[client.rid];
if (!room) {
return;
}
msg = _.trim(info.msg);
cancel = _.startsWith(msg, "/");
if (!(cancel || !room.random_type)) {
room.last_active_time = moment();
}
});
break;
cmd = msg.split(' ');
switch (cmd[0]) {
case '/help':
ygopro.stoc_send_chat(client, "YGOSrv233 指令帮助");
ygopro.stoc_send_chat(client, "/help 显示这个帮助信息");
ygopro.stoc_send_chat(client, "/roomname 显示当前房间的名字");
if (settings.modules.enable_windbot) {
ygopro.stoc_send_chat(client, "/ai 添加一个AI,/ai 角色名 可指定添加的角色");
}
if (settings.modules.tips) {
ygopro.stoc_send_chat(client, "/tip 显示一条提示");
}
......@@ -945,20 +1619,98 @@
ygopro.stoc_send_random_tip(client);
}
break;
case '/roomname':
if (client.room) {
ygopro.stoc_send_chat(client, "您当前的房间名是 " + client.room.name);
case '/ai':
if (settings.modules.enable_windbot) {
if (name = cmd[1]) {
windbot = _.sample(_.filter(settings.modules.windbots, function(w) {
return w.name === name || w.deck === name;
}));
if (!windbot) {
ygopro.stoc_send_chat(client, "未找到该AI角色或卡组", ygopro.constants.COLORS.RED);
return;
}
} else {
windbot = _.sample(settings.modules.windbots);
}
room.add_windbot(windbot);
}
break;
case '/test':
ygopro.stoc_send_hint_card_to_room(client.room, 2333365);
case '/roomname':
if (room) {
ygopro.stoc_send_chat(client, "您当前的房间名是 " + room.name, ygopro.constants.COLORS.BABYBLUE);
}
}
if (!(room && room.random_type)) {
return cancel;
}
if (client.abuse_count >= 5) {
log.warn("BANNED CHAT", client.name, client.ip, msg);
ygopro.stoc_send_chat(client, "您已被禁言!", ygopro.constants.COLORS.RED);
return true;
}
oldmsg = msg;
if (_.any(settings.ban.badword_level3, function(badword) {
var regexp;
regexp = new RegExp(badword, 'i');
return msg.match(regexp);
}, msg)) {
log.warn("BAD WORD LEVEL 3", client.name, client.ip, oldmsg);
cancel = true;
if (client.abuse_count > 0) {
ygopro.stoc_send_chat(client, "您的发言存在严重不适当的内容,禁止您使用随机对战功能!", ygopro.constants.COLORS.RED);
ROOM_ban_player(client.name, client.ip, "发言违规");
ROOM_ban_player(client.name, client.ip, "发言违规", 3);
client.destroy();
return true;
} else {
client.abuse_count = client.abuse_count + 4;
ygopro.stoc_send_chat(client, "您的发言存在不适当的内容,发送失败!", ygopro.constants.COLORS.RED);
}
} else if (_.any(settings.ban.badword_level2, function(badword) {
var regexp;
regexp = new RegExp(badword, 'i');
return msg.match(regexp);
}, msg)) {
log.warn("BAD WORD LEVEL 2", client.name, client.ip, oldmsg);
client.abuse_count = client.abuse_count + 3;
ygopro.stoc_send_chat(client, "您的发言存在不适当的内容,发送失败!", ygopro.constants.COLORS.RED);
cancel = true;
} else {
_.each(settings.ban.badword_level1, function(badword) {
var regexp;
regexp = new RegExp(badword, "ig");
msg = msg.replace(regexp, "**");
}, msg);
if (oldmsg !== msg) {
log.warn("BAD WORD LEVEL 1", client.name, client.ip, oldmsg);
client.abuse_count = client.abuse_count + 1;
ygopro.stoc_send_chat(client, "请使用文明用语!");
struct = ygopro.structs["chat"];
struct._setBuff(buffer);
struct.set("msg", msg);
buffer = struct.buffer;
} else if (_.any(settings.ban.badword_level0, function(badword) {
var regexp;
regexp = new RegExp(badword, 'i');
return msg.match(regexp);
}, msg)) {
log.info("BAD WORD LEVEL 0", client.name, client.ip, oldmsg);
}
}
if (client.abuse_count >= 5) {
ygopro.stoc_send_chat_to_room(room, client.name + " 已被禁言!", ygopro.constants.COLORS.RED);
ROOM_ban_player(client.name, client.ip, "发言违规");
}
return cancel;
});
ygopro.ctos_follow('UPDATE_DECK', false, function(buffer, info, client, server) {
var i, main, side;
main = (function() {
ygopro.ctos_follow('UPDATE_DECK', true, function(buffer, info, client, server) {
var buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, k, l, len, len1, line, room, struct;
room = ROOM_all[client.rid];
if (!room) {
return false;
}
buff_main = (function() {
var k, ref, results;
results = [];
for (i = k = 0, ref = info.mainc; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
......@@ -966,7 +1718,7 @@
}
return results;
})();
side = (function() {
buff_side = (function() {
var k, ref, ref1, results;
results = [];
for (i = k = ref = info.mainc, ref1 = info.mainc + info.sidec; ref <= ref1 ? k < ref1 : k > ref1; i = ref <= ref1 ? ++k : --k) {
......@@ -974,105 +1726,246 @@
}
return results;
})();
client.main = main;
client.side = side;
client.main = buff_main;
client.side = buff_side;
if (room.random_type) {
if (client.is_host) {
room.waiting_for_player = room.waiting_for_player2;
}
room.last_active_time = moment();
} else if (!room.started && room.hostinfo.mode === 1 && settings.modules.tournament_mode.enabled) {
struct = ygopro.structs["deck"];
struct._setBuff(buffer);
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
buffer = struct.buffer;
found_deck = false;
decks = fs.readdirSync(settings.modules.tournament_mode.deck_path);
for (k = 0, len = decks.length; k < len; k++) {
deck = decks[k];
if (_.endsWith(deck, client.name + ".ydk")) {
found_deck = deck;
}
if (_.endsWith(deck, client.name + ".ydk.ydk")) {
found_deck = deck;
}
}
if (found_deck) {
deck_text = fs.readFileSync(settings.modules.tournament_mode.deck_path + found_deck, {
encoding: "ASCII"
});
deck_array = deck_text.split("\n");
deck_main = [];
deck_side = [];
current_deck = deck_main;
for (l = 0, len1 = deck_array.length; l < len1; l++) {
line = deck_array[l];
if (line.indexOf("!side") >= 0) {
current_deck = deck_side;
}
card = parseInt(line);
if (!isNaN(card)) {
current_deck.push(card);
}
}
if (_.isEqual(buff_main, deck_main) && _.isEqual(buff_side, deck_side)) {
deckbuf = deck_main.concat(deck_side);
struct.set("mainc", deck_main.length);
struct.set("sidec", deck_side.length);
struct.set("deckbuf", deckbuf);
buffer = struct.buffer;
ygopro.stoc_send_chat(client, "成功使用卡组 " + found_deck + " 参加比赛。", ygopro.constants.COLORS.BABYBLUE);
} else {
ygopro.stoc_send_chat(client, "您的卡组与报名卡组 " + found_deck + " 不符。注意卡组不能有包括卡片顺序在内的任何修改。", ygopro.constants.COLORS.RED);
}
} else {
ygopro.stoc_send_chat(client, client.name + ",没有找到您的报名信息,请确定您使用昵称与报名ID一致。", ygopro.constants.COLORS.RED);
}
}
return false;
});
ygopro.ctos_follow('RESPONSE', false, function(buffer, info, client, server) {
if (!(client.room && client.room.random_type)) {
var room;
room = ROOM_all[client.rid];
if (!(room && room.random_type)) {
return;
}
client.room.last_active_time = moment();
room.last_active_time = moment();
});
ygopro.ctos_follow('HAND_RESULT', false, function(buffer, info, client, server) {
if (!(client.room && client.room.random_type)) {
var room;
room = ROOM_all[client.rid];
if (!(room && room.random_type)) {
return;
}
if (client.is_host) {
client.room.waiting_for_player = client.room.waiting_for_player2;
room.waiting_for_player = room.waiting_for_player2;
}
client.room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's');
room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's');
});
ygopro.ctos_follow('TP_RESULT', false, function(buffer, info, client, server) {
if (!(client.room && client.room.random_type)) {
var room;
room = ROOM_all[client.rid];
if (!(room && room.random_type)) {
return;
}
client.room.last_active_time = moment();
room.last_active_time = moment();
});
ygopro.stoc_follow('SELECT_HAND', false, function(buffer, info, client, server) {
if (!(client.room && client.room.random_type)) {
var room;
room = ROOM_all[client.rid];
if (!(room && room.random_type)) {
return;
}
if (client.is_host) {
client.room.waiting_for_player = client;
room.waiting_for_player = client;
} else {
client.room.waiting_for_player2 = client;
room.waiting_for_player2 = client;
}
client.room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's');
room.last_active_time = moment().subtract(settings.modules.hang_timeout - 19, 's');
});
ygopro.stoc_follow('SELECT_TP', false, function(buffer, info, client, server) {
if (!(client.room && client.room.random_type)) {
var room;
room = ROOM_all[client.rid];
if (!(room && room.random_type)) {
return;
}
client.room.waiting_for_player = client;
client.room.last_active_time = moment();
room.waiting_for_player = client;
room.last_active_time = moment();
});
setInterval(function() {
var k, len, ref, room, time_passed;
ref = Room.all;
ygopro.stoc_follow('CHANGE_SIDE', false, function(buffer, info, client, server) {
var room;
room = ROOM_all[client.rid];
if (!(room && room.random_type)) {
return;
}
if (client.is_host) {
room.waiting_for_player = client;
} else {
room.waiting_for_player2 = client;
}
room.last_active_time = moment();
});
ygopro.stoc_follow('REPLAY', true, function(buffer, info, client, server) {
var duellog, player, room;
room = ROOM_all[client.rid];
if (!room) {
return settings.modules.tournament_mode.enabled;
}
if (settings.modules.enable_cloud_replay && room.random_type) {
Cloud_replay_ids.push(room.cloud_replay_id);
}
if (settings.modules.tournament_mode.enabled) {
if (client.is_host) {
duellog = {
time: moment().format('YYYY-MM-DD HH:mm:ss'),
name: room.name,
roomid: room.port.toString(),
cloud_replay_id: "R#" + room.cloud_replay_id,
players: (function() {
var k, len, ref, results;
ref = room.players;
results = [];
for (k = 0, len = ref.length; k < len; k++) {
room = ref[k];
player = ref[k];
results.push({
name: player.name,
winner: player.pos === room.winner
});
}
return results;
})()
};
settings.modules.tournament_mode.duel_log.push(duellog);
nconf.myset(settings, "modules:tournament_mode:duel_log", settings.modules.tournament_mode.duel_log);
}
if (settings.modules.enable_cloud_replay) {
ygopro.stoc_send_chat(client, "本场比赛云录像:R#" + room.cloud_replay_id + "。将于本局结束后可播放。", ygopro.constants.COLORS.BABYBLUE);
}
return true;
} else {
return false;
}
});
if (settings.modules.enable_random_duel) {
setInterval(function() {
var k, len, room, time_passed;
for (k = 0, len = ROOM_all.length; k < len; k++) {
room = ROOM_all[k];
if (!(room && room.started && room.random_type && room.last_active_time && room.waiting_for_player)) {
continue;
}
time_passed = Math.floor((moment() - room.last_active_time) / 1000);
if (time_passed >= settings.modules.hang_timeout) {
room.last_active_time = moment();
Room.ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "挂机");
ygopro.stoc_send_chat_to_room(room, room.waiting_for_player.name + " 被系统请出了房间", 11);
room.waiting_for_player.server.end();
ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "挂机");
ygopro.stoc_send_chat_to_room(room, room.waiting_for_player.name + " 被系统请出了房间", ygopro.constants.COLORS.RED);
room.waiting_for_player.server.destroy();
} else if (time_passed >= (settings.modules.hang_timeout - 20) && !(time_passed % 10)) {
ygopro.stoc_send_chat_to_room(room, room.waiting_for_player.name + " 已经很久没有操作了,若继续挂机,将于" + (settings.modules.hang_timeout - time_passed) + "秒后被请出房间", 11);
ygopro.stoc_send_chat_to_room(room, room.waiting_for_player.name + " 已经很久没有操作了,若继续挂机,将于" + (settings.modules.hang_timeout - time_passed) + "秒后被请出房间", ygopro.constants.COLORS.RED);
}
}
}, 1000);
}
if (settings.modules.spawn_windbot) {
windbot_process = spawn('mono', ['WindBot.exe', settings.modules.windbot_port], {
cwd: 'windbot'
});
windbot_process.on('error', function(err) {
log.warn('WindBot ERROR', err);
});
windbot_process.on('exit', function(code) {
log.warn('WindBot EXIT', code);
});
windbot_process.stdout.setEncoding('utf8');
windbot_process.stdout.on('data', function(data) {
log.info('WindBot:', data);
});
windbot_process.stderr.on('data', function(data) {
log.warn('WindBot Error:', data);
});
}
if (settings.modules.http) {
requestListener = function(request, response) {
var k, len, parseQueryString, pass_validated, player, ref, room, roomsjson, u;
var duellog, k, len, parseQueryString, pass_validated, player, room, roomsjson, u;
parseQueryString = true;
u = url.parse(request.url, parseQueryString);
pass_validated = u.query.pass === settings.modules.http.password;
if (u.pathname === '/api/getrooms') {
if (u.query.pass && !pass_validated) {
if (!pass_validated) {
response.writeHead(200);
response.end(u.query.callback + '( {"rooms":[{"roomid":"0","roomname":"密码错误","needpass":"true"}]} );');
} else {
response.writeHead(200);
roomsjson = JSON.stringify({
rooms: (function() {
var k, len, ref, results;
ref = Room.all;
var k, len, results;
results = [];
for (k = 0, len = ref.length; k < len; k++) {
room = ref[k];
if (room.established) {
for (k = 0, len = ROOM_all.length; k < len; k++) {
room = ROOM_all[k];
if (room && room.established) {
results.push({
pid: room.process.pid.toString(),
roomid: room.port.toString(),
roomname: pass_validated ? room.name : room.name.split('$', 2)[0],
needpass: (room.name.indexOf('$') !== -1).toString(),
users: (function() {
var l, len1, ref1, results1;
ref1 = room.players;
var l, len1, ref, results1;
ref = room.players;
results1 = [];
for (l = 0, len1 = ref1.length; l < len1; l++) {
player = ref1[l];
for (l = 0, len1 = ref.length; l < len1; l++) {
player = ref[l];
if (player.pos != null) {
results1.push({
id: (-1).toString(),
......@@ -1092,35 +1985,57 @@
});
response.end(u.query.callback + "( " + roomsjson + " );");
}
} else if (u.pathname === '/api/duellog' && settings.modules.tournament_mode.enabled) {
if (!pass_validated) {
response.writeHead(200);
response.end("密码错误");
return;
} else {
response.writeHead(200);
duellog = JSON.stringify(settings.modules.tournament_mode.duel_log);
response.end(u.query.callback + "( " + duellog + " );");
}
} else if (u.pathname === '/api/message') {
if (!pass_validated) {
response.writeHead(200);
response.end(u.query.callback + "( '密码错误', 0 );");
response.end(u.query.callback + "( ['密码错误', 0] );");
return;
}
if (u.query.shout) {
ref = Room.all;
for (k = 0, len = ref.length; k < len; k++) {
room = ref[k];
ygopro.stoc_send_chat_to_room(room, u.query.shout, 16);
for (k = 0, len = ROOM_all.length; k < len; k++) {
room = ROOM_all[k];
if (room && room.established) {
ygopro.stoc_send_chat_to_room(room, u.query.shout, ygopro.constants.COLORS.YELLOW);
}
}
response.writeHead(200);
response.end(u.query.callback + "( 'shout ok', '" + u.query.shout + "' );");
response.end(u.query.callback + "( ['shout ok', '" + u.query.shout + "'] );");
} else if (u.query.stop) {
if (u.query.stop === 'false') {
u.query.stop = false;
}
settings.modules.stop = u.query.stop;
response.writeHead(200);
response.end(u.query.callback + "( 'stop ok', '" + u.query.stop + "' );");
response.end(u.query.callback + "( ['stop ok', '" + u.query.stop + "'] );");
} else if (u.query.welcome) {
settings.modules.welcome = u.query.welcome;
nconf.myset(settings, 'modules:welcome', u.query.welcome);
response.writeHead(200);
response.end(u.query.callback + "( ['welcome ok', '" + u.query.welcome + "'] );");
} else if (u.query.getwelcome) {
response.writeHead(200);
response.end(u.query.callback + "( ['get ok', '" + settings.modules.welcome + "'] );");
} else if (u.query.loadtips) {
load_tips();
response.writeHead(200);
response.end(u.query.callback + "( ['loading tip', '" + settings.modules.tips + "'] );");
} else if (u.query.loaddialogues) {
load_dialogues();
response.writeHead(200);
response.end(u.query.callback + "( 'welcome ok', '" + u.query.welcome + "' );");
response.end(u.query.callback + "( ['loading dialogues', '" + settings.modules.dialogues + "'] );");
} else if (u.query.ban) {
settings.BANNED_user.push(u.query.ban);
ban_user(u.query.ban);
response.writeHead(200);
response.end(u.query.callback + "( 'ban ok', '" + u.query.ban + "' );");
response.end(u.query.callback + "( ['ban ok', '" + u.query.ban + "'] );");
} else {
response.writeHead(404);
response.end();
......
/*
ygopro-tournament.js
ygopro tournament util
Author: mercury233
License: MIT
不带参数运行时,会建立一个服务器,调用API执行对应操作
*/
var http = require('http');
var fs = require('fs');
var url = require('url');
var request = require('request');
var formidable = require('formidable');
var _ = require('underscore');
_.str = require('underscore.string');
_.mixin(_.str.exports());
var nconf = require('nconf');
nconf.file('./config.user.json');
var defaultconfig = require('./config.json');
nconf.defaults(defaultconfig);
var settings = nconf.get();
config=settings.modules.tournament_mode;
//http长连接
var responder;
config.wallpapers=[""];
request({
url: "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8&mkt=zh-CN",
json: true
}, function(error, response, body) {
if (_.isString(body)) {
console.log("wallpapers bad json", body);
}
else if (error || !body) {
console.log('wallpapers error', error, response);
}
else {
config.wallpapers=[];
for (var i in body.images) {
var wallpaper=body.images[i];
var img={
"url": "http://s.cn.bing.net"+wallpaper.urlbase+"_768x1366.jpg",
"desc": wallpaper.copyright
}
config.wallpapers.push(img);
}
}
});
//输出反馈信息,如有http长连接则输出到http,否则输出到控制台
var sendResponse = function(text) {
text=""+text;
if (responder) {
text=text.replace(/\n/g,"<br>");
responder.write("data: " + text + "\n\n");
}
else {
console.log(text);
}
}
//读取指定卡组
var readDeck = function(deck_name, deck_full_path) {
var deck={};
deck.name=deck_name;
deck_text = fs.readFileSync(deck_full_path, { encoding: "ASCII" });
deck_array = deck_text.split("\n");
deck.main = [];
deck.extra = [];
deck.side = [];
current_deck = deck.main;
for (l in deck_array) {
line = deck_array[l];
if (line.indexOf("#extra") >= 0) {
current_deck = deck.extra;
}
if (line.indexOf("!side") >= 0) {
current_deck = deck.side;
}
card = parseInt(line);
if (!isNaN(card)) {
current_deck.push(card);
}
}
return deck;
}
//读取指定文件夹中所有卡组
var getDecks = function() {
var decks=[];
var decks_list = fs.readdirSync(config.deck_path);
for (var k in decks_list) {
var deck_name = decks_list[k];
if (_.endsWith(deck_name, ".ydk")) {
var deck = readDeck(deck_name, config.deck_path+deck_name);
decks.push(deck);
}
}
return decks;
}
var delDeck = function(deck_name) {
var result=0;
try {
fs.unlinkSync(config.deck_path+deck_name);
result="已删除"+deck_name+"";
}
catch(e) {
result=e.toString();
}
finally {
return result;
}
}
var clearDecks = function() {
var decks_list = fs.readdirSync(config.deck_path);
for (var k in decks_list) {
var deck_name = decks_list[k];
if (_.endsWith(deck_name, ".ydk")) {
delDeck(deck_name);
}
}
}
var receiveDecks = function(files) {
var result=[];
for (var i in files) {
var file=files[i];
if (_.endsWith(file.name, ".ydk")) {
var deck=readDeck(file.name, file.path);
if (deck.main.length>=40) {
fs.createReadStream(file.path).pipe(fs.createWriteStream(config.deck_path+file.name));
result.push({
file: file.name,
status: "OK"
});
}
else {
result.push({
file: file.name,
status: "卡组不合格"
});
}
}
else {
result.push({
file: file.name,
status: "不是卡组文件"
});
}
}
return result;
}
//建立一个http服务器,接收API操作
http.createServer(function (req, res) {
var u = url.parse(req.url, true);
if (u.query.password !== config.password) {
res.writeHead(403);
res.end("Auth Failed.");
return;
}
if (u.pathname === '/api/upload_decks' && req.method.toLowerCase() == 'post') {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {
"Access-Control-Allow-origin": "*",
'content-type': 'text/plain'
});
var result=receiveDecks(files);
//console.log(files);
res.end(JSON.stringify(result));
});
}
else if (u.pathname === '/api/msg') {
res.writeHead(200, {
"Access-Control-Allow-origin": "*",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive"
});
res.on("close", function(){
responder = null;
});
responder = res;
sendResponse("已连接。");
}
else if (u.pathname === '/api/get_bg') {
res.writeHead(200);
res.end(u.query.callback+'('+JSON.stringify(config.wallpapers[Math.floor(Math.random() * config.wallpapers.length)])+');');
}
else if (u.pathname === '/api/get_decks') {
res.writeHead(200);
var decklist=getDecks();
res.end(u.query.callback+'('+JSON.stringify(decklist)+');');
}
else if (u.pathname === '/api/del_deck') {
res.writeHead(200);
var result=delDeck(u.query.msg);
res.end(u.query.callback+'("'+result+'");');
}
else if (u.pathname === '/api/clear_decks') {
res.writeHead(200);
clearDecks();
res.end(u.query.callback+'("已删除全部卡组。");');
}
else {
res.writeHead(400);
res.end("400");
}
}).listen(config.port);
_ = require 'underscore'
_.str = require 'underscore.string'
_.mixin(_.str.exports());
_.mixin(_.str.exports())
Struct = require('struct').Struct
Struct = require('./struct.js').Struct
#常量/类型声明
structs_declaration = require './structs.json' #结构体声明
......@@ -137,17 +137,26 @@ for name, declaration of structs_declaration
console.log "err stoc_send_hint_card_to_room"
return
for client in room.players
@stoc_send client, 'GAME_MSG',{
@stoc_send client, 'GAME_MSG', {
curmsg: 2,
type: 10,
player: 0,
data: card
} if client
for client in room.watchers
@stoc_send client, 'GAME_MSG',{
@stoc_send client, 'GAME_MSG', {
curmsg: 2,
type: 10,
player: 0,
data: card
} if client
return
@stoc_die = (client, msg)->
@stoc_send_chat(client, msg, @constants.COLORS.RED)
@stoc_send client, 'ERROR_MSG', {
msg: 1
code: 2
} if client
client.destroy() if client
return
\ No newline at end of file
......@@ -8,7 +8,7 @@
_.mixin(_.str.exports());
Struct = require('struct').Struct;
Struct = require('./struct.js').Struct;
structs_declaration = require('./structs.json');
......@@ -245,4 +245,17 @@
}
};
this.stoc_die = function(client, msg) {
this.stoc_send_chat(client, msg, this.constants.COLORS.RED);
if (client) {
this.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 2
});
}
if (client) {
client.destroy();
}
};
}).call(this);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment