Commit dae63867 authored by nanahira's avatar nanahira

Merge branch 'master' into tcg_random

parents 1505e18f f6845f79
# ignore
package-lock.json
jsconfig.json
coffeelint.json
.vscode/
......
# Dockerfile for SRVPro
FROM node:stretch
RUN ssh-keygen -A
RUN sed -i 's/deb.debian.org/ftp.cn.debian.org/g' /etc/apt/sources.list
RUN apt update
RUN apt install -y openssh-server locales curl git vim sudo cron build-essential premake4 libevent-dev libsqlite3-dev liblua5.3-dev mono-complete sqlite3 p7zip-full redis-server
RUN ln -s /usr/lib/x86_64-linux-gnu/liblua5.3.so /usr/lib/liblua.so
RUN npm install pm2 coffeescript@1.12.7 -g
# 系统源
#RUN sed -i 's/deb.debian.org/ftp.cn.debian.org/g' /etc/apt/sources.list
#RUN apt update
# ssh
RUN mkdir -p /var/run/sshd
RUN mkdir /root/.ssh
RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
# locale
RUN echo "zh_CN.UTF-8 UTF-8" > /etc/locale.gen && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
locale-gen && \
dpkg-reconfigure -f noninteractive locales tzdata && \
/usr/sbin/update-locale LANG=zh_CN.UTF-8
ENV LANG=zh_CN.UTF-8
# declarations
EXPOSE 22
# apt
RUN apt update && \
env DEBIAN_FRONTEND=noninteractive apt install -y curl wget vim sudo git build-essential libssl1.0-dev libsqlite3-dev sqlite3 mono-complete p7zip-full redis-server
RUN npm install -g pm2
# libevent
WORKDIR /
RUN wget 'https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz' -O libevent-2.0.22-stable.tar.gz --no-check-certificate && \
tar xf libevent-2.0.22-stable.tar.gz && \
cd libevent-2.0.22-stable/ && \
./configure && \
make && \
make install && \
cd .. && \
bash -c 'ln -s /usr/local/lib/libevent-2.0.so.5 /usr/lib/libevent-2.0.so.5;ln -s /usr/local/lib/libevent_pthreads-2.0.so.5 /usr/lib/libevent_pthreads-2.0.so.5;ln -s /usr/local/lib/libevent-2.0.so.5 /usr/lib64/libevent-2.0.so.5;ln -s /usr/local/lib/libevent_pthreads-2.0.so.5 /usr/lib64/libevent_pthreads-2.0.so.5;exit 0'
# srvpro
COPY . /ygopro-server
WORKDIR /ygopro-server
RUN npm ci && \
mkdir config decks replays logs && \
cp data/default_config.json config/config.json
# ygopro
RUN git clone --branch=server --recursive https://github.com/purerosefallen/ygopro /ygopro-server/ygopro
WORKDIR /ygopro-server/ygopro
RUN git submodule foreach git checkout master && \
wget -O - https://github.com/premake/premake-core/releases/download/v5.0.0-alpha13/premake-5.0.0-alpha13-linux.tar.gz | tar zfx - && \
./premake5 gmake && \
cd build && \
make config=release && \
cd .. && \
ln -s ./bin/release/ygopro . && \
strip ygopro && \
mkdir replay expansions
# windbot
RUN git clone https://github.com/purerosefallen/windbot /ygopro-server/windbot
WORKDIR /ygopro-server/windbot
RUN xbuild /property:Configuration=Release /property:TargetFrameworkVersion="v4.5" && \
ln -s ./bin/Release/WindBot.exe . && \
ln -s /ygopro-server/ygopro/cards.cdb .
# infos
WORKDIR /
RUN mkdir /redis
EXPOSE 7911
EXPOSE 7922
VOLUME /root
VOLUME /ygopro-server/config
VOLUME /ygopro-server/ygopro/expansions
WORKDIR /root
COPY data/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD [ "/entrypoint.sh" ]
CMD [ "pm2-docker", "start", "/ygopro-server/data/pm2-docker.json" ]
{
"file": "./config/admin_user.json",
"permission_examples": {
"sudo": {
"get_rooms": true,
"duel_log": true,
"download_replay": true,
"clear_duel_log": true,
"deck_dashboard_read": true,
"deck_dashboard_write": true,
"shout": true,
"stop": true,
"change_settings": true,
"ban_user": true,
"kick_user": true,
"start_death": true,
"pre_dashboard": true,
"update_dashboard": true,
"vip": true
},
"judge": {
"get_rooms": true,
"duel_log": true,
"download_replay": true,
"deck_dashboard_read": true,
"deck_dashboard_write": true,
"shout": true,
"kick_user": true,
"start_death": true
},
"streamer": {
"get_rooms": true,
"duel_log": true,
"download_replay": true,
"deck_dashboard_read": true
}
},
"users": {
"root": {
"password": "Nanahira",
"enabled": true,
"permissions": "sudo"
}
}
}
......@@ -159,7 +159,6 @@
"show_ip": true,
"show_info": true,
"log_save_path": "./config/",
"password": "Nanahira",
"port": 7212
},
"test_mode": {
......@@ -170,7 +169,6 @@
"pre_util": {
"enabled": false,
"port": 7944,
"password": "123456",
"git_html_path": "../mercury233.github.io/",
"html_path": "../mercury233.github.io/ygosrv233/",
"html_filename": "pre.html",
......@@ -263,7 +261,6 @@
"update_util": {
"enabled": false,
"port": 7955,
"password": "123456",
"git_html_path": "../ygo233-web/",
"html_path": "../ygo233-web/",
"cdb_path": "./ygopro/cards.cdb",
......@@ -289,7 +286,6 @@
},
"http": {
"port": 7211,
"password": "Nanahira",
"websocket_roomlist": false,
"public_roomlist": false,
"show_ip": true,
......
{
"file": "./config/admin_user.json",
"permission_examples": {
"sudo": {
"get_rooms": true,
"duel_log": true,
"download_replay": true,
"clear_duel_log": true,
"deck_dashboard_read": true,
"deck_dashboard_write": true,
"shout": true,
"stop": true,
"change_settings": true,
"ban_user": true,
"kick_user": true,
"start_death": true,
"pre_dashboard": true,
"update_dashboard": true,
"vip": true
},
"judge": {
"get_rooms": true,
"duel_log": true,
"download_replay": true,
"deck_dashboard_read": true,
"deck_dashboard_write": true,
"shout": true,
"kick_user": true,
"start_death": true
},
"streamer": {
"get_rooms": true,
"duel_log": true,
"download_replay": true,
"deck_dashboard_read": true
}
},
"users": {
"root": {
"password": "Nanahira",
"enabled": true,
"permissions": "sudo"
},
"judge": {
"password": "Clala",
"enabled": true,
"permissions": "judge"
},
"streamer": {
"password": "Aris",
"enabled": true,
"permissions": "streamer"
}
}
}
......@@ -162,7 +162,6 @@
"show_ip": true,
"show_info": true,
"log_save_path": "./config/",
"password": "Nanahira",
"port": 1264
},
"test_mode": {
......@@ -173,7 +172,6 @@
"pre_util": {
"enabled": false,
"port": 7944,
"password": "123456",
"git_html_path": "../mercury233.github.io/",
"html_path": "../mercury233.github.io/ygosrv233/",
"html_filename": "pre.html",
......@@ -263,7 +261,6 @@
"update_util": {
"enabled": false,
"port": 7955,
"password": "123456",
"git_html_path": "../ygo233-web/",
"html_path": "../ygo233-web/",
"cdb_path": "./ygopro/cards.cdb",
......@@ -289,7 +286,6 @@
},
"http": {
"port": 1263,
"password": "Nanahira",
"websocket_roomlist": false,
"public_roomlist": true,
"show_ip": true,
......
......@@ -22,6 +22,7 @@
"side_timeout": false,
"tag_duel_surrender": true,
"replay_delay": false,
"hide_name": false,
"i18n": {
"auto_pick": false,
"default": "zh-cn",
......@@ -161,6 +162,7 @@
"comment": "mode: athletic / entertain",
"accesskey": "233",
"ready_time": 30,
"check_permit": "https://api.mycard.moe/ygopro/match/permit",
"post_score": false,
"get_score": false
},
......
#!/bin/bash
if [ -n "$authorized_keys" ] && [ ! -f /root/.ssh/authorized_keys ]; then mkdir /root/.ssh; printenv authorized_keys > /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys; fi
if [ -n "$password" ] && passwd --status | grep -q 'L'; then echo "root:$password" | chpasswd ; fi
unset authorized_keys
unset password
if [ -s /root/.pm2/dump.pm2 ]; then pm2 resurrect; fi
/usr/sbin/sshd -D
......@@ -190,6 +190,8 @@
"replay_hint_part1": "Sending the replay of the duel number ",
"replay_hint_part2": ".",
"invalid_side_rule": "Illegal cards are contained in your side deck.",
"arena_wait_hint": "If you opponent does not appear within 25 seconds, you may quit without any penalty.",
"arena_wait_timeout": "Your opponent did not appear, you may quit without any penalty.",
"athletic_arena_tip": "During an athletic match, a game quit behavior is regarded as a surrender."
},
"es-es": {
......@@ -527,6 +529,8 @@
"replay_hint_part1": "正在发送第",
"replay_hint_part2": "局决斗的录像。",
"invalid_side_rule": "副卡组中包含不允许换入副卡组的卡。",
"arena_wait_hint": "若对手在25秒内不进入游戏,您退房时不会进行扣分。",
"arena_wait_timeout": "由于对手未能在30秒内进入游戏,此时您退出游戏不会扣分。",
"athletic_arena_tip": "在竞技匹配中,比赛开始前退出游戏也会视为投降。"
},
"ko-kr": {
......
{
"apps": [
{
"name": "ygopro-server",
"script": "/ygopro-server/ygopro-server.js",
"cwd": "/ygopro-server"
},
{
"name": "windbot",
"script": "/ygopro-server/windbot/WindBot.exe",
"cwd": "/ygopro-server/windbot/",
"args": "ServerMode=true ServerPort=2399",
"interpreter": "mono"
},
{
"name": "redis-server",
"script": "/usr/bin/redis-server",
"cwd": "/redis"
}
]
}
This diff is collapsed.
......@@ -24,7 +24,7 @@
"geoip-country-lite": "latest",
"challonge": "latest",
"pg": "^6.4.2",
"ws": "^6.0.0"
"ws": "^1.1.1"
},
"license": "AGPL-3.0",
"scripts": {
......
......@@ -8,6 +8,7 @@
TODO:带参数运行时执行对应操作后退出
*/
var http = require('http');
var https = require('https');
var sqlite3 = require('sqlite3').verbose();
var fs = require('fs');
var execSync = require('child_process').execSync;
......@@ -22,7 +23,8 @@ var auth = require('./ygopro-auth.js');
var constants = loadJSON('./data/constants.json');
var settings = loadJSON('./config/config.json');
config=settings.modules.pre_util;
config = settings.modules.pre_util;
ssl_config = settings.modules.http.ssl;
//全卡HTML列表
var cardHTMLs=[];
......@@ -405,7 +407,7 @@ var packDatas = function () {
}
//建立一个http服务器,接收API操作
http.createServer(function (req, res) {
function requestListener(req, res) {
var u = url.parse(req.url, true);
if (!auth.auth(u.query.username, u.query.password, "pre_dashboard", "pre_dashboard")) {
......@@ -465,4 +467,16 @@ http.createServer(function (req, res) {
res.end("400");
}
}).listen(config.port);
}
if (ssl_config.enabled) {
const ssl_cert = fs.readFileSync(ssl_config.cert);
const ssl_key = fs.readFileSync(ssl_config.key);
const options = {
cert: ssl_cert,
key: ssl_key
}
https.createServer(options, requestListener).listen(config.port);
} else {
http.createServer(requestListener).listen(config.port);
}
......@@ -72,6 +72,8 @@ import_datas = [
"start_deckbuf",
"challonge_info",
"ready_trap",
"join_time",
"arena_quit_free",
"replays_sent"
]
......@@ -1507,6 +1509,7 @@ class Room
connect: (client)->
@players.push client
client.join_time = moment()
if @random_type
client.abuse_count = 0
host_player = @get_host()
......@@ -1541,7 +1544,7 @@ class Room
if @arena and !@started and @disconnector != 'server' and !@arena_score_handled
for player in @players when player.pos != 7
@scores[player.name_vpass] = 0
if @players.length == 2
if @players.length == 2 and !client.arena_quit_free
@scores[client.name_vpass] = -9
@arena_score_handled = true
index = _.indexOf(@players, client)
......@@ -1555,7 +1558,8 @@ class Room
if settings.modules.random_duel.record_match_scores and @random_type == 'M'
ROOM_player_flee(client.name_vpass)
if @players.length and !(@windbot and client.is_host) and !(@arena and !@started and client.pos <= 3)
ygopro.stoc_send_chat_to_room this, "#{client.name} ${left_game}" + if error then ": #{error}" else ''
left_name = (if settings.modules.hide_name and !@started then "********" else client.name)
ygopro.stoc_send_chat_to_room this, "#{left_name} ${left_game}" + if error then ": #{error}" else ''
roomlist.update(this) if !@windbot and !@started and settings.modules.http.websocket_roomlist
#client.room = null
else
......@@ -2042,13 +2046,13 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
ygopro.stoc_die(client, '${invalid_password_payload}')
return
check = (buf)->
check_buffer_indentity = (buf)->
checksum = 0
for i in [0...buf.length]
checksum += buf.readUInt8(i)
(checksum & 0xFF) == 0
finish = (buffer)->
buffer_handle_callback = (buffer, decrypted_buffer, match_permit)->
if client.closed
return
action = buffer.readUInt8(1) >> 4
......@@ -2095,6 +2099,9 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
ygopro.stoc_die(client, '${invalid_password_not_found}')
return
when 4
if match_permit and !match_permit.permit
ygopro.stoc_die(client, '${invalid_password_unauthorized}')
return
room = ROOM_find_or_create_by_name('M#' + info.pass.slice(8))
if room
for player in room.get_playing_player() when player and player.name == client.name
......@@ -2146,13 +2153,16 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
room.connect(client)
return
match_permit_callback = (buffer, match_permit) ->
if client.closed
return
if id = users_cache[client.name]
secret = id % 65535 + 1
decrypted_buffer = Buffer.allocUnsafe(6)
for i in [0, 2, 4]
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i)
if check(decrypted_buffer)
return finish(decrypted_buffer)
if check_buffer_indentity(decrypted_buffer)
return buffer_handle_callback(decrypted_buffer, decrypted_buffer, match_permit)
#TODO: query database directly, like preload.
request
......@@ -2170,16 +2180,36 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
decrypted_buffer = Buffer.allocUnsafe(6)
for i in [0, 2, 4]
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i)
if check(decrypted_buffer)
if check_buffer_indentity(decrypted_buffer)
buffer = decrypted_buffer
# buffer != decrypted_buffer ==> auth failed
if !check(buffer)
if !check_buffer_indentity(buffer)
ygopro.stoc_die(client, '${invalid_password_checksum}')
return
return buffer_handle_callback(buffer, decrypted_buffer, match_permit)
return
if settings.modules.arena_mode.check_permit
request
url: settings.modules.arena_mode.check_permit,
json: true,
qs:
username: client.name,
password: info.pass
, (error, response, body)->
if client.closed
return
if !error and body
match_permit_callback(buffer, body)
else
log.warn("Match permit request error", error)
match_permit_callback(buffer, null)
return
else
match_permit_callback(buffer, null)
finish(buffer)
else if settings.modules.challonge.enabled
pre_room = ROOM_find_by_name(info.pass)
......@@ -2811,6 +2841,17 @@ ygopro.stoc_follow 'TYPE_CHANGE', true, (buffer, info, client, server, datas)->
#console.log "TYPE_CHANGE to #{client.name}:", info, selftype, is_host
return false
ygopro.stoc_follow 'HS_PLAYER_ENTER', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
return false unless room and settings.modules.hide_name and !room.started
pos = info.pos
if pos < 4 and pos != client.pos
struct = ygopro.structs["STOC_HS_PlayerEnter"]
struct._setBuff(buffer)
struct.set("name", "********")
buffer = struct.buffer
return false
ygopro.stoc_follow 'HS_PLAYER_CHANGE', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
return unless room and room.max_player and client.is_host
......@@ -2904,7 +2945,9 @@ wait_room_start_arena = (room)->
room.waiting_for_player_time = room.waiting_for_player_time - 1
if room.waiting_for_player_time > 0
unless room.waiting_for_player_time % 5
ygopro.stoc_send_chat_to_room(room, "#{if room.waiting_for_player_time <= 9 then ' ' else ''}#{room.waiting_for_player_time}${kick_count_down_arena_part1} #{room.waiting_for_player.name} ${kick_count_down_arena_part2}", if room.waiting_for_player_time <= 9 then ygopro.constants.COLORS.RED else ygopro.constants.COLORS.LIGHTBLUE)
for player in room.players when player
display_name = (if settings.modules.hide_name and player != room.waiting_for_player then "********" else room.waiting_for_player.name)
ygopro.stoc_send_chat(player, "#{if room.waiting_for_player_time <= 9 then ' ' else ''}#{room.waiting_for_player_time}${kick_count_down_arena_part1} #{display_name} ${kick_count_down_arena_part2}", if room.waiting_for_player_time <= 9 then ygopro.constants.COLORS.RED else ygopro.constants.COLORS.LIGHTBLUE)
else
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_kick(room.waiting_for_player)
......@@ -2987,6 +3030,12 @@ ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
if room.random_type == 'T'
# 双打房不记录匹配过
ROOM_players_oppentlist[player.ip] = null
if settings.modules.hide_name and room.duel_count == 0
for player in room.get_playing_player() when player != client
ygopro.stoc_send(client, 'HS_PLAYER_ENTER', {
name: player.name,
pos: player.pos
})
if settings.modules.tips.enabled
ygopro.stoc_send_random_tip(client)
deck_text = null
......@@ -3549,7 +3598,7 @@ ygopro.ctos_follow 'TP_RESULT', false, (buffer, info, client, server, datas)->
ygopro.stoc_follow 'CHAT', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
pid = info.player
return unless room and pid < 4 and settings.modules.chat_color.enabled
return unless room and pid < 4 and settings.modules.chat_color.enabled and (!settings.modules.hide_name or room.started)
if room.started and room.turn > 0 and !room.dueling_players[0].is_first
if room.hostinfo.mode == 2
pid = {
......@@ -3721,6 +3770,15 @@ if settings.modules.mycard.enabled
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)
for room in ROOM_all when room and room.arena and !room.started 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
......@@ -3784,7 +3842,8 @@ if settings.modules.http
#console.log(u.query.username, u.query.pass)
if u.pathname == '/api/getrooms'
if !settings.modules.http.public_roomlist and !auth.auth(u.query.username, u.query.pass, "get_rooms", "get_rooms")
pass_validated = auth.auth(u.query.username, u.query.pass, "get_rooms", "get_rooms")
if !settings.modules.http.public_roomlist and !pass_validated
response.writeHead(200)
response.end(addCallback(u.query.callback, '{"rooms":[{"roomid":"0","roomname":"密码错误","needpass":"true"}]}'))
else
......
This diff is collapsed.
......@@ -7,6 +7,7 @@
不带参数运行时,会建立一个服务器,调用API执行对应操作
*/
var http = require('http');
var https = require('https');
var fs = require('fs');
var url = require('url');
var request = require('request');
......@@ -19,8 +20,9 @@ var loadJSON = require('load-json-file').sync;
var auth = require('./ygopro-auth.js');
var settings = loadJSON('./config/config.json');
config=settings.modules.tournament_mode;
challonge_config=settings.modules.challonge;
config = settings.modules.tournament_mode;
challonge_config = settings.modules.challonge;
ssl_config = settings.modules.http.ssl;
var challonge;
if (challonge_config.enabled) {
......@@ -214,7 +216,7 @@ var receiveDecks = function(files) {
}
//建立一个http服务器,接收API操作
http.createServer(function (req, res) {
function requestListener(req, res) {
var u = url.parse(req.url, true);
/*if (u.query.password !== config.password) {
......@@ -315,4 +317,16 @@ http.createServer(function (req, res) {
res.end("400");
}
}).listen(config.port);
}
if (ssl_config.enabled) {
const ssl_cert = fs.readFileSync(ssl_config.cert);
const ssl_key = fs.readFileSync(ssl_config.key);
const options = {
cert: ssl_cert,
key: ssl_key
}
https.createServer(options, requestListener).listen(config.port);
} else {
http.createServer(requestListener).listen(config.port);
}
......@@ -8,6 +8,7 @@
TODO:带参数运行时执行对应操作后退出
*/
var http = require('http');
var https = require('https');
var sqlite3 = require('sqlite3').verbose();
var fs = require('fs');
var execSync = require('child_process').execSync;
......@@ -23,7 +24,8 @@ var auth = require('./ygopro-auth.js');
var constants = loadJSON('./data/constants.json');
var settings = loadJSON('./config/config.json');
config=settings.modules.update_util;
config = settings.modules.update_util;
ssl_config = settings.modules.http.ssl;
//全卡名称列表
var cardNames={};
......@@ -212,7 +214,7 @@ var pushHTMLs = function() {
//建立一个http服务器,接收API操作
http.createServer(function (req, res) {
function requestListener(req, res) {
var u = url.parse(req.url, true);
if (!auth.auth(u.query.username, u.query.password, "update_dashboard", "update_dashboard")) {
......@@ -275,4 +277,16 @@ http.createServer(function (req, res) {
res.end("400");
}
}).listen(config.port);
}
if (ssl_config.enabled) {
const ssl_cert = fs.readFileSync(ssl_config.cert);
const ssl_key = fs.readFileSync(ssl_config.key);
const options = {
cert: ssl_cert,
key: ssl_key
}
https.createServer(options, requestListener).listen(config.port);
} else {
http.createServer(requestListener).listen(config.port);
}
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