Commit 04888860 authored by nanahira's avatar nanahira

rework server init

parent de15fb02
......@@ -59,7 +59,7 @@ add_log = (message) ->
text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n"
res = false
try
await util.promisify(fs.appendFile)("./logs/"+mt.format('YYYY-MM-DD')+".log", text)
await fs.promises.appendFile("./logs/"+mt.format('YYYY-MM-DD')+".log", text)
res = true
catch
res = false
......@@ -69,7 +69,7 @@ add_log = (message) ->
default_data = loadJSON('./data/default_data.json')
setting_save = (settings) ->
try
await util.promisify(fs.writeFile)(settings.file, JSON.stringify(settings, null, 2))
await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2))
catch e
add_log("save fail");
return
......
......@@ -73,7 +73,7 @@
text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n";
res = false;
try {
await util.promisify(fs.appendFile)("./logs/" + mt.format('YYYY-MM-DD') + ".log", text);
await fs.promises.appendFile("./logs/" + mt.format('YYYY-MM-DD') + ".log", text);
res = true;
} catch (error) {
res = false;
......@@ -86,7 +86,7 @@
setting_save = async function(settings) {
var e;
try {
await util.promisify(fs.writeFile)(settings.file, JSON.stringify(settings, null, 2));
await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2));
} catch (error) {
e = error;
add_log("save fail");
......
......@@ -21,6 +21,7 @@ request = require 'request'
axios = require 'axios'
qs = require "querystring"
zlib = require 'zlib'
axios = require 'axios'
bunyan = require 'bunyan'
log = global.log = bunyan.createLogger name: "mycard"
......@@ -83,54 +84,24 @@ merge = require 'deepmerge'
loadJSON = require('load-json-file').sync
loadJSONAsync = require('load-json-file')
util = require("util")
Q = require("q")
#heapdump = require 'heapdump'
# 配置
# 导入旧配置
if not fs.existsSync('./config')
fs.mkdirSync('./config')
try
oldconfig=loadJSON('./config.user.json')
if oldconfig.tips
oldtips = {}
oldtips.file = './config/tips.json'
oldtips.tips = oldconfig.tips
fs.writeFileSync(oldtips.file, JSON.stringify(oldtips, null, 2))
delete oldconfig.tips
if oldconfig.dialogues
olddialogues = {}
olddialogues.file = './config/dialogues.json'
olddialogues.dialogues = oldconfig.dialogues
fs.writeFileSync(olddialogues.file, JSON.stringify(olddialogues, null, 2))
delete oldconfig.dialogues
oldbadwords={}
if oldconfig.ban
if oldconfig.ban.badword_level0
oldbadwords.level0 = oldconfig.ban.badword_level0
if oldconfig.ban.badword_level1
oldbadwords.level1 = oldconfig.ban.badword_level1
if oldconfig.ban.badword_level2
oldbadwords.level2 = oldconfig.ban.badword_level2
if oldconfig.ban.badword_level3
oldbadwords.level3 = oldconfig.ban.badword_level3
if not _.isEmpty(oldbadwords)
oldbadwords.file = './config/badwords.json'
fs.writeFileSync(oldbadwords.file, JSON.stringify(oldbadwords, null, 2))
delete oldconfig.ban.badword_level0
delete oldconfig.ban.badword_level1
delete oldconfig.ban.badword_level2
delete oldconfig.ban.badword_level3
if not _.isEmpty(oldconfig)
# log.info oldconfig
fs.writeFileSync('./config/config.json', JSON.stringify(oldconfig, null, 2))
log.info 'imported old config from config.user.json'
fs.renameSync('./config.user.json', './config.user.bak')
catch e
log.info e unless e.code == 'ENOENT'
checkFileExists = (path) =>
try
await fs.promises.access(path)
return true
catch e
return false
createDirectoryIfNotExists = (path) =>
if !await checkFileExists(path)
await fs.promises.mkdir(path, {recursive: true})
setting_save = global.setting_save = (settings) ->
try
......@@ -155,218 +126,82 @@ setting_change = global.setting_change = (settings, path, val) ->
await setting_save(settings)
return
# 读取配置
default_config = loadJSON('./data/default_config.json')
if fs.existsSync('./config/config.json')
importOldConfig = () ->
try
config = loadJSON('./config/config.json')
oldconfig=await loadJSONAsync('./config.user.json')
if oldconfig.tips
oldtips = {}
oldtips.file = './config/tips.json'
oldtips.tips = oldconfig.tips
await fs.promises.writeFile(oldtips.file, JSON.stringify(oldtips, null, 2))
delete oldconfig.tips
if oldconfig.dialogues
olddialogues = {}
olddialogues.file = './config/dialogues.json'
olddialogues.dialogues = oldconfig.dialogues
await fs.promises.writeFile(olddialogues.file, JSON.stringify(olddialogues, null, 2))
delete oldconfig.dialogues
oldbadwords={}
if oldconfig.ban
if oldconfig.ban.badword_level0
oldbadwords.level0 = oldconfig.ban.badword_level0
if oldconfig.ban.badword_level1
oldbadwords.level1 = oldconfig.ban.badword_level1
if oldconfig.ban.badword_level2
oldbadwords.level2 = oldconfig.ban.badword_level2
if oldconfig.ban.badword_level3
oldbadwords.level3 = oldconfig.ban.badword_level3
if not _.isEmpty(oldbadwords)
oldbadwords.file = './config/badwords.json'
await fs.promises.writeFile(oldbadwords.file, JSON.stringify(oldbadwords, null, 2))
delete oldconfig.ban.badword_level0
delete oldconfig.ban.badword_level1
delete oldconfig.ban.badword_level2
delete oldconfig.ban.badword_level3
if not _.isEmpty(oldconfig)
# log.info oldconfig
await fs.promises.writeFile('./config/config.json', JSON.stringify(oldconfig, null, 2))
log.info 'imported old config from config.user.json'
await fs.promises.rename('./config.user.json', './config.user.bak')
catch e
console.error("Failed reading config: ", e.toString())
process.exit(1)
else
config = {}
settings = global.settings = merge(default_config, config, { arrayMerge: (destination, source) -> source })
log.info e unless e.code == 'ENOENT'
auth = global.auth = require './ygopro-auth.js'
#import old configs
imported = false
#reset http.quick_death_rule from true to 1
if settings.modules.http.quick_death_rule == true
settings.modules.http.quick_death_rule = 1
imported = true
#import the old passwords to new admin user system
if settings.modules.http.password
auth.add_user("olduser", settings.modules.http.password, true, {
"get_rooms": true,
"shout": true,
"stop": true,
"change_settings": true,
"ban_user": true,
"kick_user": true,
"start_death": true
})
delete settings.modules.http.password
imported = true
if settings.modules.tournament_mode.password
auth.add_user("tournament", settings.modules.tournament_mode.password, true, {
"duel_log": true,
"download_replay": true,
"clear_duel_log": true,
"deck_dashboard_read": true,
"deck_dashboard_write": true,
})
delete settings.modules.tournament_mode.password
imported = true
if settings.modules.pre_util.password
auth.add_user("pre", settings.modules.pre_util.password, true, {
"pre_dashboard": true
})
delete settings.modules.pre_util.password
imported = true
if settings.modules.update_util.password
auth.add_user("update", settings.modules.update_util.password, true, {
"update_dashboard": true
})
delete settings.modules.update_util.password
imported = true
#import the old enable_priority hostinfo
if settings.hostinfo.enable_priority or settings.hostinfo.enable_priority == false
if settings.hostinfo.enable_priority
settings.hostinfo.duel_rule = 3
else
settings.hostinfo.duel_rule = 5
delete settings.hostinfo.enable_priority
imported = true
#import the old Challonge api key option
if settings.modules.challonge.api_key
settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key
delete settings.modules.challonge.api_key
imported = true
#import the old random_duel.blank_pass_match option
if settings.modules.random_duel.blank_pass_match == true
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":true,"T":false}
delete settings.modules.random_duel.blank_pass_match
imported = true
if settings.modules.random_duel.blank_pass_match == false
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":false,"T":false}
delete settings.modules.random_duel.blank_pass_match
imported = true
#finish
if imported
setting_save(settings)
# 读取数据
default_data = loadJSON('./data/default_data.json')
try
tips = global.tips = loadJSON('./config/tips.json')
catch
tips = global.tips = default_data.tips
setting_save(tips)
try
dialogues = global.dialogues = loadJSON('./config/dialogues.json')
catch
dialogues = global.dialogues = default_data.dialogues
setting_save(dialogues)
try
badwords = global.badwords = loadJSON('./config/badwords.json')
catch
badwords = global.badwords = default_data.badwords
setting_save(badwords)
try
chat_color = global.chat_color = loadJSON('./config/chat_color.json')
catch
chat_color = global.chat_color = default_data.chat_color
setting_save(chat_color)
try
cppversion = parseInt(fs.readFileSync('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
setting_change(settings, "version", cppversion)
log.info "ygopro version 0x"+settings.version.toString(16), "(from source code)"
catch
#settings.version = settings.version_default
log.info "ygopro version 0x"+settings.version.toString(16), "(from config)"
# load the lflist of current date
lflists = global.lflists = []
# expansions/lflist
try
for list in fs.readFileSync('ygopro/expansions/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
# lflist
try
for list in fs.readFileSync('ygopro/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
if settings.modules.windbot.enabled
windbots = global.windbots = loadJSON(settings.modules.windbot.botlist).windbots
real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip
if !settings.modules.windbot.server_ip.includes("127.0.0.1")
dns = require('dns')
dns.lookup(settings.modules.windbot.server_ip,(err,addr) ->
if(!err)
real_windbot_server_ip = global.real_windbot_server_ip = addr
)
if settings.modules.heartbeat_detection.enabled
long_resolve_cards = global.long_resolve_cards = loadJSON('./data/long_resolve_cards.json')
if settings.modules.tournament_mode.enable_recover
ReplayParser = global.ReplayParser = require "./Replay.js"
if settings.modules.athletic_check.enabled
AthleticChecker = require("./athletic-check.js").AthleticChecker
athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check)
# 组件
ygopro = global.ygopro = require './ygopro.js'
roomlist = global.roomlist = require './roomlist.js' if settings.modules.http.websocket_roomlist
if settings.modules.i18n.auto_pick
geoip = require('geoip-country-lite')
roomlist = null
# cache users of mycard login
settings = {}
tips = null
dialogues = null
badwords = null
lflists = global.lflists = []
real_windbot_server_ip = null
long_resolve_cards = []
ReplayParser = null
athleticChecker = null
users_cache = {}
if settings.modules.mysql.enabled
DataManager = require('./data-manager/DataManager.js').DataManager
dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log)
dataManager.init().then(() -> log.info("Database ready."))
else
log.warn("Some functions may be limited without MySQL .")
if settings.modules.cloud_replay.enabled
settings.modules.cloud_replay.enabled = false
setting_save(settings)
log.warn("Cloud replay cannot be enabled because no MySQL.")
if settings.modules.enable_recover.enabled
settings.modules.enable_recover.enabled = false
setting_save(settings)
log.warn("Recover mode cannot be enabled because no MySQL.")
if settings.modules.chat_color.enabled
settings.modules.chat_color.enabled = false
setting_save(settings)
log.warn("Chat color cannot be enabled because no MySQL.")
if settings.modules.mycard.enabled
pgClient = require('pg').Client
pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database)
pg_client.on 'error', (err) ->
log.warn "PostgreSQL ERROR: ", err
return
pg_query = pg_client.query('SELECT username, id from users')
pg_query.on 'error', (err) ->
log.warn "PostgreSQL Query ERROR: ", err
return
pg_query.on 'row', (row) ->
#log.info "load user", row.username, row.id
users_cache[row.username] = row.id
return
pg_query.on 'end', (result) ->
log.info "users loaded", result.rowCount
return
pg_client.on 'drain', pg_client.end.bind(pg_client)
log.info "loading mycard user..."
pg_client.connect()
if settings.modules.arena_mode.enabled and settings.modules.arena_mode.init_post.enabled
request.post { url : settings.modules.arena_mode.init_post.url , qs : {
ak: settings.modules.arena_mode.init_post.accesskey,
arena: settings.modules.arena_mode.mode
}}, (error, response, body)=>
if error
log.warn 'ARENA INIT POST ERROR', error
else
if response.statusCode >= 400
log.warn 'ARENA INIT POST FAIL', response.statusCode, response.statusMessage, body
#else
# log.info 'ARENA INIT POST OK', response.statusCode, response.statusMessage
return
geoip = null
dataManager = null
disconnect_list = {} # {old_client, old_server, room_id, timeout, deckbuf}
challonge = null
challonge_cache = {
participants: null
matches: null
}
challonge_queue_callbacks = {
participants: []
matches: []
}
is_challonge_requesting = {
participants: null
matches: null
}
get_callback = () ->
replaced_index = () ->
refresh_challonge_cache = () ->
class ResolveData
constructor: (@func) ->
......@@ -378,99 +213,401 @@ class ResolveData
@func(err, data)
return true
if settings.modules.challonge.enabled
challonge_module_name = 'challonge'
if settings.modules.challonge.use_custom_module
challonge_module_name = settings.modules.challonge.use_custom_module
challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options)
if settings.modules.challonge.cache_ttl
loadLFList = (path) ->
try
for list in fs.promises.readFile(path, 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
init = () ->
await createDirectoryIfNotExists("./config")
await importOldConfig()
defaultConfig = await loadJSONAsync('./data/default_config.json')
if await checkFileExists("./config/config.json")
try
config = await loadJSONAsync('./config/config.json')
catch e
console.error("Failed reading config: ", e.toString())
process.exit(1)
else
config = {}
settings = global.settings = merge(defaultConfig, config, { arrayMerge: (destination, source) -> source })
#import old configs
imported = false
#reset http.quick_death_rule from true to 1
if settings.modules.http.quick_death_rule == true
settings.modules.http.quick_death_rule = 1
imported = true
#import the old passwords to new admin user system
if settings.modules.http.password
await auth.add_user("olduser", settings.modules.http.password, true, {
"get_rooms": true,
"shout": true,
"stop": true,
"change_settings": true,
"ban_user": true,
"kick_user": true,
"start_death": true
})
delete settings.modules.http.password
imported = true
if settings.modules.tournament_mode.password
await auth.add_user("tournament", settings.modules.tournament_mode.password, true, {
"duel_log": true,
"download_replay": true,
"clear_duel_log": true,
"deck_dashboard_read": true,
"deck_dashboard_write": true,
})
delete settings.modules.tournament_mode.password
imported = true
if settings.modules.pre_util.password
await auth.add_user("pre", settings.modules.pre_util.password, true, {
"pre_dashboard": true
})
delete settings.modules.pre_util.password
imported = true
if settings.modules.update_util.password
await auth.add_user("update", settings.modules.update_util.password, true, {
"update_dashboard": true
})
delete settings.modules.update_util.password
imported = true
#import the old enable_priority hostinfo
if settings.hostinfo.enable_priority or settings.hostinfo.enable_priority == false
if settings.hostinfo.enable_priority
settings.hostinfo.duel_rule = 3
else
settings.hostinfo.duel_rule = 5
delete settings.hostinfo.enable_priority
imported = true
#import the old Challonge api key option
if settings.modules.challonge.api_key
settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key
delete settings.modules.challonge.api_key
imported = true
#import the old random_duel.blank_pass_match option
if settings.modules.random_duel.blank_pass_match == true
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":true,"T":false}
delete settings.modules.random_duel.blank_pass_match
imported = true
if settings.modules.random_duel.blank_pass_match == false
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":false,"T":false}
delete settings.modules.random_duel.blank_pass_match
imported = true
#finish
if imported
await setting_save(settings)
# 读取数据
default_data = await loadJSONAsync('./data/default_data.json')
try
tips = global.tips = await loadJSONAsync('./config/tips.json')
catch
tips = global.tips = default_data.tips
await setting_save(tips)
try
dialogues = global.dialogues = await loadJSONAsync('./config/dialogues.json')
catch
dialogues = global.dialogues = default_data.dialogues
await setting_save(dialogues)
try
badwords = global.badwords = await loadJSONAsync('./config/badwords.json')
catch
badwords = global.badwords = default_data.badwords
await setting_save(badwords)
if settings.modules.chat_color.enabled
try
chat_color = global.chat_color = await loadJSONAsync('./config/chat_color.json')
catch
chat_color = global.chat_color = default_data.chat_color
await setting_save(chat_color)
try
cppversion = parseInt(await fs.promises.readFile('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
await setting_change(settings, "version", cppversion)
log.info "ygopro version 0x"+settings.version.toString(16), "(from source code)"
catch
#settings.version = settings.version_default
log.info "ygopro version 0x"+settings.version.toString(16), "(from config)"
# load the lflist of current date
await loadLFList('ygopro/expansions/lflist.conf')
await loadLFList('ygopro/lflist.conf')
if settings.modules.windbot.enabled
windbots = global.windbots = await loadJSONAsync(settings.modules.windbot.botlist).windbots
real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip
if !settings.modules.windbot.server_ip.includes("127.0.0.1")
dns = require('dns')
real_windbot_server_ip = global.real_windbot_server_ip = await util.promisify(dns.lookup)(settings.modules.windbot.server_ip)
if settings.modules.heartbeat_detection.enabled
long_resolve_cards = global.long_resolve_cards = await loadJSONAsync('./data/long_resolve_cards.json')
if settings.modules.tournament_mode.enable_recover
ReplayParser = global.ReplayParser = require "./Replay.js"
if settings.modules.athletic_check.enabled
AthleticChecker = require("./athletic-check.js").AthleticChecker
athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check)
if settings.modules.http.websocket_roomlist
roomlist = global.roomlist = require './roomlist.js'
if settings.modules.i18n.auto_pick
geoip = require('geoip-country-lite')
if settings.modules.mysql.enabled
DataManager = require('./data-manager/DataManager.js').DataManager
dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log)
await dataManager.init()
else
log.warn("Some functions may be limited without MySQL .")
if settings.modules.cloud_replay.enabled
settings.modules.cloud_replay.enabled = false
await setting_save(settings)
log.warn("Cloud replay cannot be enabled because no MySQL.")
if settings.modules.enable_recover.enabled
settings.modules.enable_recover.enabled = false
await setting_save(settings)
log.warn("Recover mode cannot be enabled because no MySQL.")
if settings.modules.chat_color.enabled
settings.modules.chat_color.enabled = false
await setting_save(settings)
log.warn("Chat color cannot be enabled because no MySQL.")
if settings.modules.mycard.enabled
pgClient = require('pg').Client
pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database)
pg_client.on 'error', (err) ->
log.warn "PostgreSQL ERROR: ", err
return
pg_query = pg_client.query('SELECT username, id from users')
pg_query.on 'error', (err) ->
log.warn "PostgreSQL Query ERROR: ", err
return
pg_query.on 'row', (row) ->
#log.info "load user", row.username, row.id
users_cache[row.username] = row.id
return
pg_query.on 'end', (result) ->
log.info "users loaded", result.rowCount
return
pg_client.on 'drain', pg_client.end.bind(pg_client)
log.info "loading mycard user..."
pg_client.connect()
if settings.modules.arena_mode.enabled and settings.modules.arena_mode.init_post.enabled
postData = qs.stringify({
ak: settings.modules.arena_mode.init_post.accesskey,
arena: settings.modules.arena_mode.mode
})
try
await axios.post(settings.modules.arena_mode.init_post.url + "?" + postData, {
responseType: "json"
})
catch e
log.warn 'ARENA INIT POST ERROR', e
if settings.modules.challonge.enabled
challonge_module_name = 'challonge'
if settings.modules.challonge.use_custom_module
challonge_module_name = settings.modules.challonge.use_custom_module
challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options)
challonge_cache = {
participants: null
matches: null
}
challonge_queue_callbacks = {
participants: []
matches: []
}
is_challonge_requesting = {
participants: null
matches: null
}
get_callback = (challonge_type, resolve_data) ->
return ((err, data) ->
if settings.modules.challonge.cache_ttl and !err and data
challonge_cache[challonge_type] = data
is_challonge_requesting[challonge_type] = null
resolve_data.resolve(err, data)
while challonge_queue_callbacks[challonge_type].length
cur_resolve_data = challonge_queue_callbacks[challonge_type].splice(0, 1)[0]
cur_resolve_data.resolve(err, data)
challonge_queue_callbacks = {
participants: []
matches: []
}
is_challonge_requesting = {
participants: null
matches: null
}
get_callback = (challonge_type, resolve_data) ->
return ((err, data) ->
if settings.modules.challonge.cache_ttl and !err and data
challonge_cache[challonge_type] = data
is_challonge_requesting[challonge_type] = null
resolve_data.resolve(err, data)
while challonge_queue_callbacks[challonge_type].length
cur_resolve_data = challonge_queue_callbacks[challonge_type].splice(0, 1)[0]
cur_resolve_data.resolve(err, data)
return
)
replaced_index = (challonge_type) ->
return (_data) ->
resolve_data = new ResolveData(_data.callback)
if settings.modules.challonge.cache_ttl and !_data.no_cache and challonge_cache[challonge_type]
resolve_data.resolve(null, challonge_cache[challonge_type])
else if is_challonge_requesting[challonge_type] and moment() - is_challonge_requesting[challonge_type] <= 5000
challonge_queue_callbacks[challonge_type].push(resolve_data)
else
_data.callback = get_callback(challonge_type, resolve_data)
is_challonge_requesting[challonge_type] = moment()
try
challonge[challonge_type].index(_data)
catch err
_data.callback(err, null)
return
for challonge_type in ["participants", "matches"]
challonge[challonge_type]._index = replaced_index(challonge_type)
challonge.matches._update = (_data) ->
try
challonge.matches.update(_data)
catch err
log.warn("Errored pushing scores to Challonge.", err)
return
)
replaced_index = (challonge_type) ->
return (_data) ->
resolve_data = new ResolveData(_data.callback)
if settings.modules.challonge.cache_ttl and !_data.no_cache and challonge_cache[challonge_type]
resolve_data.resolve(null, challonge_cache[challonge_type])
else if is_challonge_requesting[challonge_type] and moment() - is_challonge_requesting[challonge_type] <= 5000
challonge_queue_callbacks[challonge_type].push(resolve_data)
else
_data.callback = get_callback(challonge_type, resolve_data)
is_challonge_requesting[challonge_type] = moment()
try
challonge[challonge_type].index(_data)
catch err
_data.callback(err, null)
refresh_challonge_cache = global.refresh_challonge_cache = () ->
if settings.modules.challonge.cache_ttl
challonge_cache.participants = null
challonge_cache.matches = null
return
for challonge_type in ["participants", "matches"]
challonge[challonge_type]._index = replaced_index(challonge_type)
challonge.matches._update = (_data) ->
try
challonge.matches.update(_data)
catch err
log.warn("Errored pushing scores to Challonge.", err)
return
refresh_challonge_cache = global.refresh_challonge_cache = () ->
refresh_challonge_cache()
if settings.modules.challonge.cache_ttl
challonge_cache.participants = null
challonge_cache.matches = null
setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl)
if settings.modules.tips.get
load_tips()
setInterval ()->
for room in ROOM_all when room and room.established
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING or room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
return
, 30000
if settings.modules.dialogues.get
load_dialogues()
if settings.modules.random_duel.post_match_scores
setInterval(()->
scores_pair = _.pairs ROOM_players_scores
scores_by_lose = _.sortBy(scores_pair, (score)-> return score[1].lose).reverse() # 败场由高到低
scores_by_win = _.sortBy(scores_by_lose, (score)-> return score[1].win).reverse() # 然后胜场由低到高,再逆转,就是先排胜场再排败场
scores = _.first(scores_by_win, 10)
#log.info scores
request.post { url : settings.modules.random_duel.post_match_scores , form : {
accesskey: settings.modules.random_duel.post_match_accesskey,
rank: JSON.stringify(scores)
}}, (error, response, body)=>
if error
log.warn 'RANDOM SCORE POST ERROR', error
else
if response.statusCode != 204 and response.statusCode != 200
log.warn 'RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body
#else
# log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
return
return
, 60000)
if settings.modules.random_duel.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.random_type and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}")
return
, 1000
if settings.modules.mycard.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.arena and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
return
if true # settings.modules.arena_mode.punish_quit_before_match
for room in ROOM_all when room and room.arena and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.get_playing_player().length < 2
player = room.get_playing_player()[0]
if player and player.join_time and !player.arena_quit_free
waited_time = moment() - player.join_time
if waited_time >= 30000
ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE)
player.arena_quit_free = true
else if waited_time >= 5000 and waited_time < 6000
ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE)
return
, 1000
if settings.modules.heartbeat_detection.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and (room.hostinfo.time_limit == 0 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING) and !room.windbot
for player in room.get_playing_player() when player and (room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING or player.selected_preduel)
CLIENT_heartbeat_register(player, true)
return
, settings.modules.heartbeat_detection.interval
if settings.modules.windbot.enabled and settings.modules.windbot.spawn
spawn_windbot()
setInterval ()->
current_time = moment()
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.auto_death and !room.auto_death_triggered and current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death
room.auto_death_triggered = true
room.start_death()
, 1000
net.createServer(netRequestHandler).listen settings.port, ->
log.info "server started", settings.port
return
refresh_challonge_cache()
# challonge.participants._index({
# id: settings.modules.challonge.tournament_id,
# callback: (() ->
# challonge.matches._index({
# id: settings.modules.challonge.tournament_id,
# callback: (() ->
# return
# )
# })
# return
# )
# })
if settings.modules.challonge.cache_ttl
setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl)
if settings.modules.stop
log.info "NOTE: server not open due to config, ", settings.modules.stop
http_server = http.createServer(httpRequestListener)
http_server.listen settings.modules.http.port
if settings.modules.http.ssl.enabled
https = require 'https'
options =
cert: await fs.promises.readFile(settings.modules.http.ssl.cert)
key: await fs.promises.readFile(settings.modules.http.ssl.key)
https_server = https.createServer(options, httpRequestListener)
if settings.modules.http.websocket_roomlist and roomlist
roomlist.init https_server, ROOM_all
https_server.listen settings.modules.http.ssl.port
mkdirList = [
"./plugins",
settings.modules.tournament_mode.deck_path,
settings.modules.tournament_mode.replay_path,
settings.modules.tournament_mode.log_save_path,
settings.modules.deck_log.local
]
for path in mkdirList
await createDirectoryIfNotExists(path)
plugin_list = await fs.promises.readdir("./plugins")
for plugin_filename in plugin_list
plugin_path = process.cwd() + "/plugins/" + plugin_filename
require(plugin_path)
log.info("Plugin loaded:", plugin_filename)
# 获取可用内存
memory_usage = global.memory_usage = 0
get_memory_usage = get_memory_usage = ()->
prc_free = exec("free")
prc_free.stdout.on 'data', (data)->
lines = data.toString().split(/\n/g)
line = lines[0].split(/\s+/)
new_free = if line[6] == 'available' then true else false
line = lines[1].split(/\s+/)
total = parseInt(line[1], 10)
free = parseInt(line[3], 10)
buffers = parseInt(line[5], 10)
if new_free
actualFree = parseInt(line[6], 10)
else
cached = parseInt(line[6], 10)
actualFree = free + buffers + cached
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2))
memory_usage = global.memory_usage = percentUsed
return
get_memory_usage = global.get_memory_usage = ()->
percentUsed = os.freemem() * os.totalmem() * 100
memory_usage = global.memory_usage = percentUsed
return
get_memory_usage()
setInterval(get_memory_usage, 3000)
......@@ -555,28 +692,6 @@ ROOM_player_get_score = global.ROOM_player_get_score = (player)->
return "${random_score_part1}#{player.name} ${random_score_part2} #{Math.ceil(score.win/total*100)}${random_score_part3} #{Math.ceil(score.flee/total*100)}${random_score_part4}"
return
if settings.modules.random_duel.post_match_scores
setInterval(()->
scores_pair = _.pairs ROOM_players_scores
scores_by_lose = _.sortBy(scores_pair, (score)-> return score[1].lose).reverse() # 败场由高到低
scores_by_win = _.sortBy(scores_by_lose, (score)-> return score[1].win).reverse() # 然后胜场由低到高,再逆转,就是先排胜场再排败场
scores = _.first(scores_by_win, 10)
#log.info scores
request.post { url : settings.modules.random_duel.post_match_scores , form : {
accesskey: settings.modules.random_duel.post_match_accesskey,
rank: JSON.stringify(scores)
}}, (error, response, body)=>
if error
log.warn 'RANDOM SCORE POST ERROR', error
else
if response.statusCode != 204 and response.statusCode != 200
log.warn 'RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body
#else
# log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
return
return
, 60000)
ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player_ip)->
uname=name.toUpperCase()
if settings.modules.windbot.enabled and (uname[0...2] == 'AI' or (!settings.modules.random_duel.enabled and uname == ''))
......@@ -980,9 +1095,6 @@ CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = (client, deckbuf) ->
CLIENT_reconnect_unregister(client, true)
return
if settings.modules.reconnect.enabled
disconnect_list = {} # {old_client, old_server, room_id, timeout, deckbuf}
CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = (client) ->
if !settings.modules.heartbeat_detection.enabled or !client.heartbeat_timeout
return false
......@@ -1606,7 +1718,7 @@ class Room
await return
# 网络连接
net.createServer (client) ->
netRequestHandler = (client) ->
client.ip = client.remoteAddress
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
......@@ -1786,12 +1898,6 @@ net.createServer (client) ->
return
return
.listen settings.port, ->
log.info "server started", settings.port
return
if settings.modules.stop
log.info "NOTE: server not open due to config, ", settings.modules.stop
deck_name_match = global.deck_name_match = (deck_name, player_name) ->
if deck_name == player_name or deck_name == player_name + ".ydk" or deck_name == player_name + ".ydk.ydk"
......@@ -2396,25 +2502,8 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
await return
# 登场台词
load_dialogues = global.load_dialogues = (callback) ->
request
url: settings.modules.dialogues.get
json: true
, (error, response, body)->
if _.isString body
log.warn "dialogues bad json", body
else if error or !body
log.warn 'dialogues error', error, response
else
setting_change(dialogues, "dialogues", body)
log.info "dialogues loaded", _.size dialogues.dialogues
if callback
callback(error, body)
return
await return
if settings.modules.dialogues.get
load_dialogues()
load_dialogues = global.load_dialogues = () ->
return await loadRemoteData(dialogues, "dialogues", settings.modules.dialogues.get)
ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
......@@ -2883,30 +2972,26 @@ ygopro.stoc_send_random_tip_to_room = (room)->
ygopro.stoc_send_chat_to_room(room, "Tip: " + tips.tips[Math.floor(Math.random() * tips.tips.length)])
await return
load_tips = global.load_tips = (callback)->
request
url: settings.modules.tips.get
json: true
, (error, response, body)->
loadRemoteData = global.loadRemoteData = (loadObject, name, url)->
try
body = (await axios.get(url, {
responseType: "json"
})).data
if _.isString body
log.warn "tips bad json", body
else if error or !body
log.warn 'tips error', error, response
else
setting_change(tips, "tips", body)
log.info "tips loaded", tips.tips.length
if callback
callback(error, body)
return
await return
log.warn "#{name} bad json", body
return false
if !body
log.warn "#{name} empty", body
return false
await setting_change(loadObject, name, body)
log.info "#{name} loaded"
return true
catch e
log.warn "#{name} error", e
return false
if settings.modules.tips.get
load_tips()
setInterval ()->
for room in ROOM_all when room and room.established
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING or room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
return
, 30000
load_tips = global.load_tips = ()->
return await loadRemoteData(tips, "tips", settings.modules.tips.get)
ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
......@@ -3522,70 +3607,6 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
else
await return settings.modules.replay_delay and room.hostinfo.mode == 1
if settings.modules.random_duel.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.random_type and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}")
return
, 1000
if settings.modules.mycard.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.arena and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
return
if true # settings.modules.arena_mode.punish_quit_before_match
for room in ROOM_all when room and room.arena and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.get_playing_player().length < 2
player = room.get_playing_player()[0]
if player and player.join_time and !player.arena_quit_free
waited_time = moment() - player.join_time
if waited_time >= 30000
ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE)
player.arena_quit_free = true
else if waited_time >= 5000 and waited_time < 6000
ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE)
return
, 1000
if settings.modules.heartbeat_detection.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and (room.hostinfo.time_limit == 0 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING) and !room.windbot
for player in room.get_playing_player() when player and (room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING or player.selected_preduel)
CLIENT_heartbeat_register(player, true)
return
, settings.modules.heartbeat_detection.interval
setInterval ()->
current_time = moment()
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.auto_death and !room.auto_death_triggered and current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death
room.auto_death_triggered = true
room.start_death()
, 1000
# spawn windbot
windbot_looplimit = 0
windbot_process = global.windbot_process = null
......@@ -3622,18 +3643,15 @@ spawn_windbot = global.spawn_windbot = () ->
return
return
if settings.modules.windbot.enabled and settings.modules.windbot.spawn
spawn_windbot()
global.rebooted = false
#http
if settings.modules.http
if true
addCallback = (callback, text)->
if not callback then return text
return callback + "( " + text + " );"
requestListener = (request, response)->
httpRequestListener = (request, response)->
parseQueryString = true
u = url.parse(request.url, parseQueryString)
#pass_validated = u.query.pass == settings.modules.http.password
......@@ -3823,26 +3841,24 @@ if settings.modules.http
response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return
load_tips((err)->
response.writeHead(200)
if(err)
response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"))
else
response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']"))
)
success = await load_tips()
response.writeHead(200)
if success
response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']"))
else
response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"))
else if u.query.loaddialogues
if !await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues")
response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return
load_dialogues((err)->
response.writeHead(200)
if(err)
response.end(addCallback(u.query.callback, "['dialogues fail', '" + settings.modules.dialogues.get + "']"))
else
response.end(addCallback(u.query.callback, "['dialogues ok', '" +settings.modules.dialogues.get + "']"))
)
success = await load_tips()
response.writeHead(200)
if success
response.end(addCallback(u.query.callback, "['dialogue ok', '" + settings.modules.tips.get + "']"))
else
response.end(addCallback(u.query.callback, "['dialogue fail', '" + settings.modules.tips.get + "']"))
else if u.query.ban
if !await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user")
......@@ -3942,31 +3958,4 @@ if settings.modules.http
response.end()
return
http_server = http.createServer(requestListener)
http_server.listen settings.modules.http.port
if settings.modules.http.ssl.enabled
https = require 'https'
options =
cert: fs.readFileSync(settings.modules.http.ssl.cert)
key: fs.readFileSync(settings.modules.http.ssl.key)
https_server = https.createServer(options, requestListener)
if settings.modules.http.websocket_roomlist and roomlist
roomlist.init https_server, ROOM_all
https_server.listen settings.modules.http.ssl.port
mkdirList = [
"./plugins",
settings.modules.tournament_mode.deck_path,
settings.modules.tournament_mode.replay_path,
settings.modules.tournament_mode.log_save_path,
settings.modules.deck_log.local
]
if not fs.existsSync('./plugins')
fs.mkdirSync('./plugins')
plugin_list = fs.readdirSync("./plugins")
for plugin_filename in plugin_list
plugin_path = process.cwd() + "/plugins/" + plugin_filename
require(plugin_path)
log.info("Plugin loaded:", plugin_filename)
init()
This source diff could not be displayed because it is too large. You can view the blob instead.
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