Commit c3c66c53 authored by nanahira's avatar nanahira

add recover from replay

parent e592353c
lzma = require 'lzma'
fs = require 'fs'
# Deck = require './Deck.js'
class replayHeader
@replayCompressedFlag = 0x1
@replayTagFlag = 0x2
@replayDecodedFlag = 0x4
constructor: ->
@id = 0
@version = 0
@flag = 0
@seed = 0
@dataSizeRaw = []
@hash = 0
@props = []
getDataSize: ->
@dataSizeRaw[0] + @dataSizeRaw[1] * 0x100 + @dataSizeRaw[2] * 0x10000 + @dataSizeRaw[3] * 0x1000000
getIsTag: ->
@flag & replayHeader.replayTagFlag > 0
getIsCompressed: ->
@flag & replayHeader.replayCompressedFlag > 0
getLzmaHeader: ->
bytes = [].concat(@props[0..4], @dataSizeRaw, [0, 0, 0, 0])
Buffer.from(bytes)
Object.defineProperty replayHeader.prototype, 'dataSize', get: @getDataSize
Object.defineProperty replayHeader.prototype, 'isTag', get: @getIsTag
Object.defineProperty replayHeader.prototype, 'isCompressed', get: @getIsCompressed
class ReplayReader
constructor: (buffer) ->
@pointer = 0
@buffer = buffer
readByte: ->
answer = @buffer.readUInt8(@pointer)
@pointer += 1
answer
readByteArray: (length) ->
answer = []
answer.push @readByte() for i in [1..length]
answer
readInt8: ->
answer = @buffer.readInt8(@pointer)
@pointer += 1
answer
readUInt8: ->
answer = @buffer.readUInt8(@pointer)
@pointer += 1
answer
readInt16: ->
answer = @buffer.readInt16LE @pointer
@pointer += 2
answer
readInt32: ->
answer = @buffer.readInt32LE @pointer
@pointer += 4
answer
readAll: ->
answer = @buffer.slice(@pointer)
# @pointer = 0
answer
readString: (length) ->
if @pointer + length > @buffer.length
return null
full = @buffer.slice(@pointer, @pointer + length).toString('utf-16le')
answer = full.split("\u0000")[0]
@pointer += length
answer
readRaw: (length) ->
if @pointer + length > @buffer.length
return null
answer = @buffer.slice(@pointer, @pointer + length)
@pointer += length
answer
class Replay
constructor: ->
@header = null
@hostName = ""
@clientName = ""
@startLp = 0
@startHand = 0
@drawCount = 0
@opt = 0
@hostDeck = null
@clientDeck = null
@tagHostName = null
@tagClientName = null
@tagHostDeck = null
@tagClientDeck = null
@responses = null
getDecks: ->
if @isTag
[@hostDeck, @clientDeck, @tagHostDeck, @tagClientDeck]
else
[@hostDeck, @clientDeck]
getIsTag: ->
@header == null ? false : @header.isTag
@fromFile: (filePath) ->
Replay.fromBuffer fs.readFileSync filePath
@fromBuffer: (buffer) ->
reader = new ReplayReader buffer
header = Replay.readHeader reader
lzmaBuffer = Buffer.concat [header.getLzmaHeader(), reader.readAll()]
if header.isCompressed
decompressed = lzmaBuffer
else
decompressed = Buffer.from lzma.decompress lzmaBuffer
reader = new ReplayReader decompressed
replay = Replay.readReplay header, reader
replay
@readHeader: (reader) ->
header = new replayHeader()
header.id = reader.readInt32()
header.version = reader.readInt32()
header.flag = reader.readInt32()
header.seed = reader.readInt32()
header.dataSizeRaw = reader.readByteArray 4
header.hash = reader.readInt32()
header.props = reader.readByteArray 8
header
@readReplay: (header, reader) ->
replay = new Replay()
replay.header = header
replay.hostName = reader.readString(40)
replay.tagHostName = reader.readString(40) if header.isTag
replay.tagClientName = reader.readString(40) if header.isTag
replay.clientName = reader.readString(40)
replay.startLp = reader.readInt32()
replay.startHand = reader.readInt32()
replay.drawCount = reader.readInt32()
replay.opt = reader.readInt32()
replay.hostDeck = Replay.readDeck reader
replay.tagHostDeck = Replay.readDeck reader if header.isTag
replay.tagClientDeck = Replay.readDeck reader if header.isTag
replay.clientDeck = Replay.readDeck reader
replay.responses = Replay.readResponses reader
replay
@readDeck: (reader) ->
deck = {}
deck.main = Replay.readDeckPack reader
deck.ex = Replay.readDeckPack reader
deck
@readDeckPack: (reader) ->
length = reader.readInt32()
answer = []
answer.push reader.readInt32() for i in [1..length]
answer
@readResponses: (reader) ->
answer = []
while true
try
length = reader.readUInt8()
if length > 64
length = 64
single = reader.readRaw(length)
if !single
break
answer.push(single)
catch
break
answer
Object.defineProperty replayHeader.prototype, 'decks', get: @getDecks
Object.defineProperty replayHeader.prototype, 'isTag', get: @getIsTag
module.exports = Replay
// Generated by CoffeeScript 2.5.1
(function() {
var Replay, ReplayReader, fs, lzma, replayHeader;
lzma = require('lzma');
fs = require('fs');
replayHeader = (function() {
// Deck = require './Deck.js'
class replayHeader {
constructor() {
this.id = 0;
this.version = 0;
this.flag = 0;
this.seed = 0;
this.dataSizeRaw = [];
this.hash = 0;
this.props = [];
}
getDataSize() {
return this.dataSizeRaw[0] + this.dataSizeRaw[1] * 0x100 + this.dataSizeRaw[2] * 0x10000 + this.dataSizeRaw[3] * 0x1000000;
}
getIsTag() {
return this.flag & replayHeader.replayTagFlag > 0;
}
getIsCompressed() {
return this.flag & replayHeader.replayCompressedFlag > 0;
}
getLzmaHeader() {
var bytes;
bytes = [].concat(this.props.slice(0, 5), this.dataSizeRaw, [0, 0, 0, 0]);
return Buffer.from(bytes);
}
};
replayHeader.replayCompressedFlag = 0x1;
replayHeader.replayTagFlag = 0x2;
replayHeader.replayDecodedFlag = 0x4;
Object.defineProperty(replayHeader.prototype, 'dataSize', {
get: replayHeader.getDataSize
});
Object.defineProperty(replayHeader.prototype, 'isTag', {
get: replayHeader.getIsTag
});
Object.defineProperty(replayHeader.prototype, 'isCompressed', {
get: replayHeader.getIsCompressed
});
return replayHeader;
}).call(this);
ReplayReader = class ReplayReader {
constructor(buffer) {
this.pointer = 0;
this.buffer = buffer;
}
readByte() {
var answer;
answer = this.buffer.readUInt8(this.pointer);
this.pointer += 1;
return answer;
}
readByteArray(length) {
var answer, i, j, ref;
answer = [];
for (i = j = 1, ref = length; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) {
answer.push(this.readByte());
}
return answer;
}
readInt8() {
var answer;
answer = this.buffer.readInt8(this.pointer);
this.pointer += 1;
return answer;
}
readUInt8() {
var answer;
answer = this.buffer.readUInt8(this.pointer);
this.pointer += 1;
return answer;
}
readInt16() {
var answer;
answer = this.buffer.readInt16LE(this.pointer);
this.pointer += 2;
return answer;
}
readInt32() {
var answer;
answer = this.buffer.readInt32LE(this.pointer);
this.pointer += 4;
return answer;
}
readAll() {
var answer;
answer = this.buffer.slice(this.pointer);
// @pointer = 0
return answer;
}
readString(length) {
var answer, full;
if (this.pointer + length > this.buffer.length) {
return null;
}
full = this.buffer.slice(this.pointer, this.pointer + length).toString('utf-16le');
answer = full.split("\u0000")[0];
this.pointer += length;
return answer;
}
readRaw(length) {
var answer;
if (this.pointer + length > this.buffer.length) {
return null;
}
answer = this.buffer.slice(this.pointer, this.pointer + length);
this.pointer += length;
return answer;
}
};
Replay = (function() {
class Replay {
constructor() {
this.header = null;
this.hostName = "";
this.clientName = "";
this.startLp = 0;
this.startHand = 0;
this.drawCount = 0;
this.opt = 0;
this.hostDeck = null;
this.clientDeck = null;
this.tagHostName = null;
this.tagClientName = null;
this.tagHostDeck = null;
this.tagClientDeck = null;
this.responses = null;
}
getDecks() {
if (this.isTag) {
return [this.hostDeck, this.clientDeck, this.tagHostDeck, this.tagClientDeck];
} else {
return [this.hostDeck, this.clientDeck];
}
}
getIsTag() {
var ref;
return (ref = this.header === null) != null ? ref : {
false: this.header.isTag
};
}
static fromFile(filePath) {
return Replay.fromBuffer(fs.readFileSync(filePath));
}
static fromBuffer(buffer) {
var decompressed, header, lzmaBuffer, reader, replay;
reader = new ReplayReader(buffer);
header = Replay.readHeader(reader);
lzmaBuffer = Buffer.concat([header.getLzmaHeader(), reader.readAll()]);
if (header.isCompressed) {
decompressed = lzmaBuffer;
} else {
decompressed = Buffer.from(lzma.decompress(lzmaBuffer));
}
reader = new ReplayReader(decompressed);
replay = Replay.readReplay(header, reader);
return replay;
}
static readHeader(reader) {
var header;
header = new replayHeader();
header.id = reader.readInt32();
header.version = reader.readInt32();
header.flag = reader.readInt32();
header.seed = reader.readInt32();
header.dataSizeRaw = reader.readByteArray(4);
header.hash = reader.readInt32();
header.props = reader.readByteArray(8);
return header;
}
static readReplay(header, reader) {
var replay;
replay = new Replay();
replay.header = header;
replay.hostName = reader.readString(40);
if (header.isTag) {
replay.tagHostName = reader.readString(40);
}
if (header.isTag) {
replay.tagClientName = reader.readString(40);
}
replay.clientName = reader.readString(40);
replay.startLp = reader.readInt32();
replay.startHand = reader.readInt32();
replay.drawCount = reader.readInt32();
replay.opt = reader.readInt32();
replay.hostDeck = Replay.readDeck(reader);
if (header.isTag) {
replay.tagHostDeck = Replay.readDeck(reader);
}
if (header.isTag) {
replay.tagClientDeck = Replay.readDeck(reader);
}
replay.clientDeck = Replay.readDeck(reader);
replay.responses = Replay.readResponses(reader);
return replay;
}
static readDeck(reader) {
var deck;
deck = {};
deck.main = Replay.readDeckPack(reader);
deck.ex = Replay.readDeckPack(reader);
return deck;
}
static readDeckPack(reader) {
var answer, i, j, length, ref;
length = reader.readInt32();
answer = [];
for (i = j = 1, ref = length; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) {
answer.push(reader.readInt32());
}
return answer;
}
static readResponses(reader) {
var answer, length, single;
answer = [];
while (true) {
try {
length = reader.readUInt8();
if (length > 64) {
length = 64;
}
single = reader.readRaw(length);
if (!single) {
break;
}
answer.push(single);
} catch (error) {
break;
}
}
return answer;
}
};
Object.defineProperty(replayHeader.prototype, 'decks', {
get: Replay.getDecks
});
Object.defineProperty(replayHeader.prototype, 'isTag', {
get: Replay.getIsTag
});
return Replay;
}).call(this);
module.exports = Replay;
}).call(this);
...@@ -168,7 +168,8 @@ ...@@ -168,7 +168,8 @@
"replay_safe": true, "replay_safe": true,
"replay_path": "./replays/", "replay_path": "./replays/",
"replay_archive_tool": "7z", "replay_archive_tool": "7z",
"block_replay_to_player": true, "block_replay_to_player": false,
"enable_recover": true,
"show_ip": false, "show_ip": false,
"show_info": true, "show_info": true,
"log_save_path": "./config/", "log_save_path": "./config/",
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
"cloud_replay_error": "Replay opening failed.", "cloud_replay_error": "Replay opening failed.",
"cloud_replay_playing": "Accessing cloud replay", "cloud_replay_playing": "Accessing cloud replay",
"cloud_replay_hint": "These are the recent saved replay codes, please enter the replay code at the password column to access it.", "cloud_replay_hint": "These are the recent saved replay codes, please enter the replay code at the password column to access it.",
"recover_replay_hint": "These are the recent duels, please enter the code RC[ID]%[TURN]#[ROOMNAME] at the password column to recover the duel.",
"recover_hint": "You entered a recover room. Please be ready with your deck on that duel.",
"recover_start_hint": "Started recovering...",
"blank_room_name": "Blank room name is unallowed, please fill in something.", "blank_room_name": "Blank room name is unallowed, please fill in something.",
"loading_user_info": "Loading user info...", "loading_user_info": "Loading user info...",
"invalid_password_length": "Password invalid (Invalid Length)", "invalid_password_length": "Password invalid (Invalid Length)",
...@@ -342,6 +345,11 @@ ...@@ -342,6 +345,11 @@
"cloud_replay_error": "播放录像出错", "cloud_replay_error": "播放录像出错",
"cloud_replay_playing": "正在观看云录像", "cloud_replay_playing": "正在观看云录像",
"cloud_replay_hint": "以下是您近期的云录像,密码处输入 R#录像编号 即可观看", "cloud_replay_hint": "以下是您近期的云录像,密码处输入 R#录像编号 即可观看",
"recover_replay_hint": "以下是您近期进行的决斗,密码处输入 RC决斗编号%回合数#房间号 即可创建复盘房间",
"recover_hint": "你进入了一个复盘房间,请使用复盘局的卡组准备。",
"recover_start_hint": "开始复盘...",
"recover_success": "复盘成功。请耐心等待跳到当前回合。",
"recover_fail": "复盘失败。",
"blank_room_name": "房间名不能为空,请在主机密码处填写房间名", "blank_room_name": "房间名不能为空,请在主机密码处填写房间名",
"loading_user_info": "正在读取用户信息...", "loading_user_info": "正在读取用户信息...",
"invalid_password_length": "主机密码不正确 (Invalid Length)", "invalid_password_length": "主机密码不正确 (Invalid Length)",
......
...@@ -314,6 +314,9 @@ if settings.modules.windbot.enabled ...@@ -314,6 +314,9 @@ if settings.modules.windbot.enabled
if settings.modules.heartbeat_detection.enabled if settings.modules.heartbeat_detection.enabled
long_resolve_cards = global.long_resolve_cards = loadJSON('./data/long_resolve_cards.json') 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"
# 组件 # 组件
ygopro = global.ygopro = require './ygopro.js' ygopro = global.ygopro = require './ygopro.js'
roomlist = global.roomlist = require './roomlist.js' if settings.modules.http.websocket_roomlist roomlist = global.roomlist = require './roomlist.js' if settings.modules.http.websocket_roomlist
...@@ -524,15 +527,8 @@ ROOM_kick = (name, callback)-> ...@@ -524,15 +527,8 @@ ROOM_kick = (name, callback)->
done() done()
return return
found = true found = true
if room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN room.termiate()
room.scores[room.dueling_players[0].name_vpass] = 0
room.scores[room.dueling_players[1].name_vpass] = 0
room.kicked = true
room.send_replays()
room.process.kill()
room.delete()
done() done()
return
, (err)-> , (err)->
callback(null, found) callback(null, found)
return return
...@@ -817,6 +813,8 @@ CLIENT_import_data = global.CLIENT_import_data = (client, old_client, room) -> ...@@ -817,6 +813,8 @@ CLIENT_import_data = global.CLIENT_import_data = (client, old_client, room) ->
room.waiting_for_player2 = client room.waiting_for_player2 = client
if room.selecting_tp == old_client if room.selecting_tp == old_client
room.selecting_tp = client room.selecting_tp = client
if room.determine_firstgo == old_client
room.determine_firstgo = client
for key in import_datas for key in import_datas
client[key] = old_client[key] client[key] = old_client[key]
old_client.had_new_reconnection = true old_client.had_new_reconnection = true
...@@ -1178,6 +1176,21 @@ class Room ...@@ -1178,6 +1176,21 @@ class Room
else else
@hostinfo.auto_death = 40 @hostinfo.auto_death = 40
if settings.modules.tournament_mode.enable_recover and (param = rule.match /(^|,|,)(RC|RECOVER)(\d*)%(\d*)(,|,|$)/)
@recovered = true
@recovering = true
@recover_from_turn = parseInt(param[4])
duel_log_id = parseInt(param[3])
@recover_duel_log = _.find(duel_log.duel_log, (duel) ->
return duel.id == duel_log_id and duel.roommode != 2
)
if !@recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replay_filename)
@error = "${cloud_replay_no}"
return
@recover_replay = ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replay_filename)
@recover_buffers = [[], [], [], []]
@welcome = "${recover_hint}"
@hostinfo.replay_mode = 0 # 0x1: Save the replays in file. 0x2: Block the replays to observers. @hostinfo.replay_mode = 0 # 0x1: Save the replays in file. 0x2: Block the replays to observers.
if settings.modules.tournament_mode.enabled if settings.modules.tournament_mode.enabled
...@@ -1189,6 +1202,9 @@ class Room ...@@ -1189,6 +1202,9 @@ class Room
(if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck 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] @hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode]
if @recovered
param.push(@recover_replay.header.seed)
try try
@process = spawn './ygopro', param, {cwd: 'ygopro'} @process = spawn './ygopro', param, {cwd: 'ygopro'}
@process_pid = @process.pid @process_pid = @process.pid
...@@ -1542,6 +1558,26 @@ class Room ...@@ -1542,6 +1558,26 @@ class Room
@death = 0 @death = 0
ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE)
return true return true
termiate: ->
if @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN
@scores[@dueling_players[0].name_vpass] = 0
@scores[@dueling_players[1].name_vpass] = 0
@kicked = true
@send_replays()
@process.kill()
@delete()
finish_recover: (fail) ->
if fail
ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED)
@termiate()
else
ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE)
@recovering = false
for player in @get_playing_player()
for buffer in @recover_buffers[player.pos]
ygopro.stoc_send(player, "GAME_MSG", buffer)
# 网络连接 # 网络连接
net.createServer (client) -> net.createServer (client) ->
...@@ -1694,7 +1730,7 @@ net.createServer (client) -> ...@@ -1694,7 +1730,7 @@ net.createServer (client) ->
break break
else else
if ctos_buffer.length >= 2 + ctos_message_length if ctos_buffer.length >= 2 + ctos_message_length
#console.log "CTOS", ygopro.constants.CTOS[ctos_proto] #console.log client.pos, "CTOS", ygopro.constants.CTOS[ctos_proto]
cancel = false cancel = false
if settings.modules.reconnect.enabled and client.pre_reconnecting and ygopro.constants.CTOS[ctos_proto] != 'UPDATE_DECK' if settings.modules.reconnect.enabled and client.pre_reconnecting and ygopro.constants.CTOS[ctos_proto] != 'UPDATE_DECK'
cancel = true cancel = true
...@@ -1780,7 +1816,7 @@ net.createServer (client) -> ...@@ -1780,7 +1816,7 @@ net.createServer (client) ->
break break
else else
if stoc_buffer.length >= 2 + stoc_message_length if stoc_buffer.length >= 2 + stoc_message_length
#console.log "STOC", ygopro.constants.STOC[stoc_proto] #console.log client.pos, "STOC", ygopro.constants.STOC[stoc_proto]
cancel = false cancel = false
b = stoc_buffer.slice(3, stoc_message_length - 1 + 3) b = stoc_buffer.slice(3, stoc_message_length - 1 + 3)
info = null info = null
...@@ -1933,6 +1969,29 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)-> ...@@ -1933,6 +1969,29 @@ ygopro.ctos_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
CLIENT_kick(client) CLIENT_kick(client)
return), 500 return), 500
else if info.pass.toUpperCase()=="RC" and settings.modules.tournament_mode.enable_recover
ygopro.stoc_send_chat(client,"${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE)
available_logs = duel_log.duel_log.filter((duel) ->
return duel.roommode != 2 and _.any(duel.players, (player) ->
return player.real_name == client.name_vpass
)
).slice(0, 8)
_.each(available_logs, (duel) ->
player_names = duel.players[0].real_name + (if duel.players[2] then "+" + duel.players[2].real_name else "") +
" VS " +
(if duel.players[1] then duel.players[1].real_name else "AI") +
(if duel.players[3] then "+" + duel.players[3].real_name else "")
ygopro.stoc_send_chat(client,"<#{duel.id}> #{player_names} #{duel.time}", ygopro.constants.COLORS.BABYBLUE)
)
# 强行等待异步执行完毕_(:з」∠)_
setTimeout (()->
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1
code: 9
}
CLIENT_kick(client)
return), 500
else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled
replay_id=info.pass.split("#")[1] replay_id=info.pass.split("#")[1]
if (replay_id>0 and replay_id<=9) if (replay_id>0 and replay_id<=9)
...@@ -2461,6 +2520,10 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2461,6 +2520,10 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return unless room and !client.reconnecting return unless room and !client.reconnecting
msg = buffer.readInt8(0) msg = buffer.readInt8(0)
#console.log client.pos, "MSG", ygopro.constants.MSG[msg]
if ygopro.constants.MSG[msg] == 'RETRY' and room.recovering
room.finish_recover(true)
return true
if settings.modules.retry_handle.enabled if settings.modules.retry_handle.enabled
if ygopro.constants.MSG[msg] == 'RETRY' if ygopro.constants.MSG[msg] == 'RETRY'
if !client.retry_count? if !client.retry_count?
...@@ -2492,8 +2555,14 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2492,8 +2555,14 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
# log.info(client.name, client.last_game_msg_title) # log.info(client.name, client.last_game_msg_title)
if (msg >= 10 and msg < 30) or msg == 132 or (msg >= 140 and msg <= 144) #SELECT和ANNOUNCE开头的消息 if (msg >= 10 and msg < 30) or msg == 132 or (msg >= 140 and msg <= 144) #SELECT和ANNOUNCE开头的消息
room.waiting_for_player = client if room.recovering
room.last_active_time = moment() ygopro.ctos_send(server, 'RESPONSE', room.recover_replay.responses.splice(0, 1)[0])
if !room.recover_replay.responses.length
room.finish_recover()
return true
else
room.waiting_for_player = client
room.last_active_time = moment()
#log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}") #log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}")
#log.info 'MSG', ygopro.constants.MSG[msg] #log.info 'MSG', ygopro.constants.MSG[msg]
...@@ -2511,6 +2580,8 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2511,6 +2580,8 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
ygopro.stoc_send_chat_to_room(room, "${death_start_final}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(room, "${death_start_final}", ygopro.constants.COLORS.BABYBLUE)
else else
ygopro.stoc_send_chat_to_room(room, "${death_start_extra}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(room, "${death_start_extra}", ygopro.constants.COLORS.BABYBLUE)
if room.recovering
ygopro.stoc_send_chat_to_room(room, "${recover_start_hint}", ygopro.constants.COLORS.BABYBLUE)
if client.is_first and (room.hostinfo.mode != 2 or client.pos == 0 or client.pos == 2) if client.is_first and (room.hostinfo.mode != 2 or client.pos == 0 or client.pos == 2)
room.first_list.push(client.name_vpass) room.first_list.push(client.name_vpass)
if settings.modules.retry_handle.enabled if settings.modules.retry_handle.enabled
...@@ -2527,6 +2598,8 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2527,6 +2598,8 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
if ygopro.constants.MSG[msg] == 'NEW_TURN' if ygopro.constants.MSG[msg] == 'NEW_TURN'
if client.pos == 0 if client.pos == 0
room.turn++ room.turn++
if room.recovering and room.recover_from_turn <= room.turn
room.finish_recover()
if room.death and room.death != -2 if room.death and room.death != -2
if room.turn >= room.death if room.turn >= room.death
oppo_pos = if room.hostinfo.mode == 2 then 2 else 1 oppo_pos = if room.hostinfo.mode == 2 then 2 else 1
...@@ -2572,6 +2645,9 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2572,6 +2645,9 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
ygopro.stoc_send_chat_to_room(room, "${death_remain_final}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(room, "${death_remain_final}", ygopro.constants.COLORS.BABYBLUE)
if ygopro.constants.MSG[msg] == 'WIN' and client.pos == 0 if ygopro.constants.MSG[msg] == 'WIN' and client.pos == 0
if room.recovering
room.finish_recover(true)
return true
pos = buffer.readUInt8(1) pos = buffer.readUInt8(1)
pos = 1 - pos unless client.is_first or pos == 2 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING pos = 1 - pos unless client.is_first or pos == 2 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING
pos = pos * 2 if pos >= 0 and room.hostinfo.mode == 2 pos = pos * 2 if pos >= 0 and room.hostinfo.mode == 2
...@@ -2712,7 +2788,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2712,7 +2788,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
delete room.long_resolve_chain delete room.long_resolve_chain
#登场台词 #登场台词
if settings.modules.dialogues.enabled if settings.modules.dialogues.enabled and !room.recovering
if ygopro.constants.MSG[msg] == 'SUMMONING' or ygopro.constants.MSG[msg] == 'SPSUMMONING' or ygopro.constants.MSG[msg] == 'CHAINING' if ygopro.constants.MSG[msg] == 'SUMMONING' or ygopro.constants.MSG[msg] == 'SPSUMMONING' or ygopro.constants.MSG[msg] == 'CHAINING'
card = buffer.readUInt32LE(1) card = buffer.readUInt32LE(1)
trigger_location = buffer.readUInt8(6) trigger_location = buffer.readUInt8(6)
...@@ -2726,6 +2802,12 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ...@@ -2726,6 +2802,12 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
client.ready_trap = !!(loc & 0x8) and !!(ppos & 0xa) and !!(cpos & 0x5) client.ready_trap = !!(loc & 0x8) and !!(ppos & 0xa) and !!(cpos & 0x5)
else if ygopro.constants.MSG[msg] != 'UPDATE_CARD' and ygopro.constants.MSG[msg] != 'WAITING' else if ygopro.constants.MSG[msg] != 'UPDATE_CARD' and ygopro.constants.MSG[msg] != 'WAITING'
client.ready_trap = false client.ready_trap = false
if room.recovering and client.pos < 4
if ygopro.constants.MSG[msg] != 'WAITING'
room.recover_buffers[client.pos].push(buffer)
return true
return false return false
#房间管理 #房间管理
...@@ -3227,13 +3309,25 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)-> ...@@ -3227,13 +3309,25 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
CLIENT_kick(room.dueling_players[oppo_pos - win_pos]) CLIENT_kick(room.dueling_players[oppo_pos - win_pos])
CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]) if room.hostinfo.mode == 2 CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]) if room.hostinfo.mode == 2
return true return true
struct = ygopro.structs["deck"]
struct._setBuff(buffer)
if room.random_type or room.arena if room.random_type or room.arena
if client.pos == 0 if client.pos == 0
room.waiting_for_player = room.waiting_for_player2 room.waiting_for_player = room.waiting_for_player2
room.last_active_time = moment() room.last_active_time = moment()
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering
recover_player_data = _.find(room.recover_duel_log.players, (player) ->
return player.real_name == client.name_vpass
)
if recover_player_data and _.isEqual(buffer, Buffer.from(recover_player_data.deckbuf, "base64"))
if recover_player_data.is_first
room.determine_firstgo = client
else
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED)
else if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.mode == 1 and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.deck_check and fs.readdirSync(settings.modules.tournament_mode.deck_path).length else if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.mode == 1 and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.deck_check and fs.readdirSync(settings.modules.tournament_mode.deck_path).length
struct = ygopro.structs["deck"]
struct._setBuff(buffer)
struct.set("mainc", 1) struct.set("mainc", 1)
struct.set("sidec", 1) struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470]) struct.set("deckbuf", [4392470, 4392470])
...@@ -3279,13 +3373,6 @@ ygopro.ctos_follow 'RESPONSE', false, (buffer, info, client, server, datas)-> ...@@ -3279,13 +3373,6 @@ ygopro.ctos_follow 'RESPONSE', false, (buffer, info, client, server, datas)->
ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return unless room return unless room
if settings.modules.reconnect.enabled
if client.closed
ygopro.ctos_send(server, 'TIME_CONFIRM')
return true
else
client.time_confirm_required = true
return unless settings.modules.heartbeat_detection.enabled and room.duel_stage == ygopro.constants.DUEL_STAGE.DUELING and !room.windbot
check = false check = false
if room.hostinfo.mode != 2 if room.hostinfo.mode != 2
check = (client.is_first and info.player == 0) or (!client.is_first and info.player == 1) check = (client.is_first and info.player == 0) or (!client.is_first and info.player == 1)
...@@ -3308,6 +3395,17 @@ ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)-> ...@@ -3308,6 +3395,17 @@ ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)->
cur_players[0] = cur_players[0] + 2 cur_players[0] = cur_players[0] + 2
cur_players[1] = cur_players[1] - 2 cur_players[1] = cur_players[1] - 2
check = client.pos == cur_players[info.player] check = client.pos == cur_players[info.player]
if room.recovering
if check
ygopro.ctos_send(server, 'TIME_CONFIRM')
return true
if settings.modules.reconnect.enabled
if client.closed
ygopro.ctos_send(server, 'TIME_CONFIRM')
return true
else
client.time_confirm_required = true
return unless settings.modules.heartbeat_detection.enabled and room.duel_stage == ygopro.constants.DUEL_STAGE.DUELING and !room.windbot
if check if check
CLIENT_heartbeat_register(client, false) CLIENT_heartbeat_register(client, false)
return false return false
...@@ -3315,6 +3413,8 @@ ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)-> ...@@ -3315,6 +3413,8 @@ ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)->
ygopro.ctos_follow 'TIME_CONFIRM', false, (buffer, info, client, server, datas)-> ygopro.ctos_follow 'TIME_CONFIRM', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return unless room return unless room
if room.recovered
room.recovered = false
if settings.modules.reconnect.enabled if settings.modules.reconnect.enabled
if client.waiting_for_last if client.waiting_for_last
client.waiting_for_last = false client.waiting_for_last = false
...@@ -3333,10 +3433,10 @@ ygopro.ctos_follow 'HAND_RESULT', false, (buffer, info, client, server, datas)-> ...@@ -3333,10 +3433,10 @@ ygopro.ctos_follow 'HAND_RESULT', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return unless room return unless room
client.selected_preduel = true client.selected_preduel = true
return unless room.random_type or room.arena if room.random_type or room.arena
if client.pos == 0 if client.pos == 0
room.waiting_for_player = room.waiting_for_player2 room.waiting_for_player = room.waiting_for_player2
room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's') room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's')
return return
ygopro.ctos_follow 'TP_RESULT', false, (buffer, info, client, server, datas)-> ygopro.ctos_follow 'TP_RESULT', false, (buffer, info, client, server, datas)->
...@@ -3374,30 +3474,47 @@ ygopro.stoc_follow 'CHAT', true, (buffer, info, client, server, datas)-> ...@@ -3374,30 +3474,47 @@ ygopro.stoc_follow 'CHAT', true, (buffer, info, client, server, datas)->
return true return true
return return
ygopro.stoc_follow 'SELECT_HAND', false, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'SELECT_HAND', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return unless room return false unless room
client.selected_preduel = false
if client.pos == 0 if client.pos == 0
room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER
return unless room.random_type or room.arena if room.random_type or room.arena
if client.pos == 0 if client.pos == 0
room.waiting_for_player = client room.waiting_for_player = client
else
room.waiting_for_player2 = client
room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's')
if room.determine_firstgo
ygopro.ctos_send(server, "HAND_RESULT", {
res: if client.pos == 0 then 2 else 1
})
return true
else else
room.waiting_for_player2 = client client.selected_preduel = false
room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's') return false
return
ygopro.stoc_follow 'SELECT_TP', false, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'HAND_RESULT', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return unless room return false unless room
client.selected_preduel = false return room.determine_firstgo
ygopro.stoc_follow 'SELECT_TP', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
return false unless room
room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO
room.selecting_tp = client
if room.random_type or room.arena if room.random_type or room.arena
room.waiting_for_player = client room.waiting_for_player = client
room.last_active_time = moment() room.last_active_time = moment()
return if room.determine_firstgo
ygopro.ctos_send(server, "TP_RESULT", {
res: if room.determine_firstgo == client then 1 else 0
})
return true
else
client.selected_preduel = false
room.selecting_tp = client
return false
ygopro.stoc_follow 'CHANGE_SIDE', false, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'CHANGE_SIDE', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
...@@ -3475,9 +3592,10 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)-> ...@@ -3475,9 +3592,10 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
replay_filename: replay_filename, replay_filename: replay_filename,
roommode: room.hostinfo.mode, roommode: room.hostinfo.mode,
players: (for player in room.dueling_players players: (for player in room.dueling_players
real_name: player.name, real_name: player.name_vpass,
deckbuf: player.deckbuf.toString("base64"), deckbuf: player.start_deckbuf.toString("base64"),
pos: player.pos pos: player.pos
is_first: player.is_first
name: player.name + (if settings.modules.tournament_mode.show_ip and !player.is_local then (" (IP: " + player.ip.slice(7) + ")") else "") + (if settings.modules.tournament_mode.show_info and not (room.hostinfo.mode == 2 and player.pos % 2 > 0) then (" (Score:" + room.scores[player.name_vpass] + " LP:" + (if player.lp? then player.lp else room.hostinfo.start_lp) + (if room.hostinfo.mode != 2 then (" Cards:" + (if player.card_count? then player.card_count else room.hostinfo.start_hand)) else "") + ")") else ""), name: player.name + (if settings.modules.tournament_mode.show_ip and !player.is_local then (" (IP: " + player.ip.slice(7) + ")") else "") + (if settings.modules.tournament_mode.show_info and not (room.hostinfo.mode == 2 and player.pos % 2 > 0) then (" (Score:" + room.scores[player.name_vpass] + " LP:" + (if player.lp? then player.lp else room.hostinfo.start_lp) + (if room.hostinfo.mode != 2 then (" Cards:" + (if player.card_count? then player.card_count else room.hostinfo.start_hand)) else "") + ")") else ""),
winner: player.pos == room.winner winner: player.pos == room.winner
) )
...@@ -3496,7 +3614,7 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)-> ...@@ -3496,7 +3614,7 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
if settings.modules.random_duel.enabled if settings.modules.random_duel.enabled
setInterval ()-> setInterval ()->
_async.each(ROOM_all, (room, done) -> _async.each(ROOM_all, (room, done) ->
if !(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)) if !(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)
done() done()
return return
time_passed = Math.floor((moment() - room.last_active_time) / 1000) time_passed = Math.floor((moment() - room.last_active_time) / 1000)
...@@ -3521,7 +3639,7 @@ if settings.modules.random_duel.enabled ...@@ -3521,7 +3639,7 @@ if settings.modules.random_duel.enabled
if settings.modules.mycard.enabled if settings.modules.mycard.enabled
setInterval ()-> setInterval ()->
_async.each(ROOM_all, (room, done) -> _async.each(ROOM_all, (room, done) ->
if not (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)) if not (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)
done() done()
return return
time_passed = Math.floor((moment() - room.last_active_time) / 1000) time_passed = Math.floor((moment() - room.last_active_time) / 1000)
......
// Generated by CoffeeScript 2.5.1 // Generated by CoffeeScript 2.5.1
(function() { (function() {
// 标准库 // 标准库
var CLIENT_get_authorize_key, CLIENT_get_kick_reconnect_target, CLIENT_heartbeat_register, CLIENT_heartbeat_unregister, CLIENT_import_data, CLIENT_is_able_to_kick_reconnect, CLIENT_is_able_to_reconnect, CLIENT_is_banned_by_mc, CLIENT_is_player, CLIENT_kick, CLIENT_kick_reconnect, CLIENT_pre_reconnect, CLIENT_reconnect, CLIENT_reconnect_register, CLIENT_reconnect_unregister, CLIENT_send_pre_reconnect_info, CLIENT_send_reconnect_info, CLIENT_send_replays, Cloud_replay_ids, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_banned, ROOM_players_oppentlist, ROOM_players_scores, ROOM_unwelcome, ROOM_validate, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, auth, badwords, ban_user, bunyan, challonge, challonge_cache, challonge_module_name, challonge_queue_callbacks, chat_color, config, cppversion, crypto, date, deck_name_match, default_config, default_data, dialogues, disconnect_list, dns, duel_log, e, exec, execFile, fs, geoip, get_callback, get_memory_usage, http, http_server, https, https_server, import_datas, imported, is_requesting, j, l, len, len1, len2, lflists, list, loadJSON, load_dialogues, load_tips, log, long_resolve_cards, m, memory_usage, merge, moment, net, oldbadwords, oldconfig, olddialogues, oldduellog, oldtips, options, os, path, pgClient, pg_client, pg_query, plugin_filename, plugin_list, plugin_path, real_windbot_server_ip, redis, redisdb, ref, ref1, refresh_challonge_cache, release_disconnect, report_to_big_brother, request, requestListener, roomlist, setting_change, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, url, users_cache, util, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib; var CLIENT_get_authorize_key, CLIENT_get_kick_reconnect_target, CLIENT_heartbeat_register, CLIENT_heartbeat_unregister, CLIENT_import_data, CLIENT_is_able_to_kick_reconnect, CLIENT_is_able_to_reconnect, CLIENT_is_banned_by_mc, CLIENT_is_player, CLIENT_kick, CLIENT_kick_reconnect, CLIENT_pre_reconnect, CLIENT_reconnect, CLIENT_reconnect_register, CLIENT_reconnect_unregister, CLIENT_send_pre_reconnect_info, CLIENT_send_reconnect_info, CLIENT_send_replays, Cloud_replay_ids, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_banned, ROOM_players_oppentlist, ROOM_players_scores, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, auth, badwords, ban_user, bunyan, challonge, challonge_cache, challonge_module_name, challonge_queue_callbacks, chat_color, config, cppversion, crypto, date, deck_name_match, default_config, default_data, dialogues, disconnect_list, dns, duel_log, e, exec, execFile, fs, geoip, get_callback, get_memory_usage, http, http_server, https, https_server, import_datas, imported, is_requesting, j, l, len, len1, len2, lflists, list, loadJSON, load_dialogues, load_tips, log, long_resolve_cards, m, memory_usage, merge, moment, net, oldbadwords, oldconfig, olddialogues, oldduellog, oldtips, options, os, path, pgClient, pg_client, pg_query, plugin_filename, plugin_list, plugin_path, real_windbot_server_ip, redis, redisdb, ref, ref1, refresh_challonge_cache, release_disconnect, report_to_big_brother, request, requestListener, roomlist, setting_change, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, url, users_cache, util, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib;
net = require('net'); net = require('net');
...@@ -403,6 +403,10 @@ ...@@ -403,6 +403,10 @@
long_resolve_cards = global.long_resolve_cards = loadJSON('./data/long_resolve_cards.json'); 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");
}
// 组件 // 组件
ygopro = global.ygopro = require('./ygopro.js'); ygopro = global.ygopro = require('./ygopro.js');
...@@ -686,15 +690,8 @@ ...@@ -686,15 +690,8 @@
return; return;
} }
found = true; found = true;
if (room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) { room.termiate();
room.scores[room.dueling_players[0].name_vpass] = 0; return done();
room.scores[room.dueling_players[1].name_vpass] = 0;
}
room.kicked = true;
room.send_replays();
room.process.kill();
room.delete();
done();
}, function(err) { }, function(err) {
callback(null, found); callback(null, found);
}); });
...@@ -1094,6 +1091,9 @@ ...@@ -1094,6 +1091,9 @@
if (room.selecting_tp === old_client) { if (room.selecting_tp === old_client) {
room.selecting_tp = client; room.selecting_tp = client;
} }
if (room.determine_firstgo === old_client) {
room.determine_firstgo = client;
}
for (n = 0, len3 = import_datas.length; n < len3; n++) { for (n = 0, len3 = import_datas.length; n < len3; n++) {
key = import_datas[n]; key = import_datas[n];
client[key] = old_client[key]; client[key] = old_client[key];
...@@ -1407,7 +1407,7 @@ ...@@ -1407,7 +1407,7 @@
Room = class Room { Room = class Room {
constructor(name, hostinfo) { constructor(name, hostinfo) {
var death_time, draw_count, duel_rule, lflist, param, rule, start_hand, start_lp, time_limit; var death_time, draw_count, duel_log_id, duel_rule, lflist, param, rule, start_hand, start_lp, time_limit;
this.hostinfo = hostinfo; this.hostinfo = hostinfo;
this.name = name; this.name = name;
//@alive = true //@alive = true
...@@ -1558,6 +1558,22 @@ ...@@ -1558,6 +1558,22 @@
this.hostinfo.auto_death = 40; this.hostinfo.auto_death = 40;
} }
} }
if (settings.modules.tournament_mode.enable_recover && (param = rule.match(/(^|,|,)(RC|RECOVER)(\d*)%(\d*)(,|,|$)/))) {
this.recovered = true;
this.recovering = true;
this.recover_from_turn = parseInt(param[4]);
duel_log_id = parseInt(param[3]);
this.recover_duel_log = _.find(duel_log.duel_log, function(duel) {
return duel.id === duel_log_id && duel.roommode !== 2;
});
if (!this.recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replay_filename)) {
this.error = "${cloud_replay_no}";
return;
}
this.recover_replay = ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replay_filename);
this.recover_buffers = [[], [], [], []];
this.welcome = "${recover_hint}";
}
} }
this.hostinfo.replay_mode = 0; // 0x1: Save the replays in file. 0x2: Block the replays to observers. this.hostinfo.replay_mode = 0; // 0x1: Save the replays in file. 0x2: Block the replays to observers.
if (settings.modules.tournament_mode.enabled) { if (settings.modules.tournament_mode.enabled) {
...@@ -1567,6 +1583,9 @@ ...@@ -1567,6 +1583,9 @@
this.hostinfo.replay_mode |= 0x2; this.hostinfo.replay_mode |= 0x2;
} }
param = [0, this.hostinfo.lflist, this.hostinfo.rule, this.hostinfo.mode, this.hostinfo.duel_rule, (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]; param = [0, this.hostinfo.lflist, this.hostinfo.rule, this.hostinfo.mode, this.hostinfo.duel_rule, (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];
if (this.recovered) {
param.push(this.recover_replay.header.seed);
}
try { try {
this.process = spawn('./ygopro', param, { this.process = spawn('./ygopro', param, {
cwd: 'ygopro' cwd: 'ygopro'
...@@ -2086,6 +2105,44 @@ ...@@ -2086,6 +2105,44 @@
return true; return true;
} }
termiate() {
if (this.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
this.scores[this.dueling_players[0].name_vpass] = 0;
this.scores[this.dueling_players[1].name_vpass] = 0;
}
this.kicked = true;
this.send_replays();
this.process.kill();
return this.delete();
}
finish_recover(fail) {
var buffer, len2, m, player, ref2, results;
if (fail) {
ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED);
return this.termiate();
} else {
ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE);
this.recovering = false;
ref2 = this.get_playing_player();
results = [];
for (m = 0, len2 = ref2.length; m < len2; m++) {
player = ref2[m];
results.push((function() {
var len3, n, ref3, results1;
ref3 = this.recover_buffers[player.pos];
results1 = [];
for (n = 0, len3 = ref3.length; n < len3; n++) {
buffer = ref3[n];
results1.push(ygopro.stoc_send(player, "GAME_MSG", buffer));
}
return results1;
}).call(this));
}
return results;
}
}
}; };
// 网络连接 // 网络连接
...@@ -2263,7 +2320,7 @@ ...@@ -2263,7 +2320,7 @@
} }
} else { } else {
if (ctos_buffer.length >= 2 + ctos_message_length) { if (ctos_buffer.length >= 2 + ctos_message_length) {
//console.log "CTOS", ygopro.constants.CTOS[ctos_proto] //console.log client.pos, "CTOS", ygopro.constants.CTOS[ctos_proto]
cancel = false; cancel = false;
if (settings.modules.reconnect.enabled && client.pre_reconnecting && ygopro.constants.CTOS[ctos_proto] !== 'UPDATE_DECK') { if (settings.modules.reconnect.enabled && client.pre_reconnecting && ygopro.constants.CTOS[ctos_proto] !== 'UPDATE_DECK') {
cancel = true; cancel = true;
...@@ -2383,7 +2440,7 @@ ...@@ -2383,7 +2440,7 @@
} }
} else { } else {
if (stoc_buffer.length >= 2 + stoc_message_length) { if (stoc_buffer.length >= 2 + stoc_message_length) {
//console.log "STOC", ygopro.constants.STOC[stoc_proto] //console.log client.pos, "STOC", ygopro.constants.STOC[stoc_proto]
cancel = false; cancel = false;
b = stoc_buffer.slice(3, stoc_message_length - 1 + 3); b = stoc_buffer.slice(3, stoc_message_length - 1 + 3);
info = null; info = null;
...@@ -2539,7 +2596,7 @@ ...@@ -2539,7 +2596,7 @@
}); });
ygopro.ctos_follow('JOIN_GAME', false, function(buffer, info, client, server, datas) { ygopro.ctos_follow('JOIN_GAME', false, function(buffer, info, client, server, datas) {
var check_buffer_indentity, create_room_with_action, len2, len3, m, n, name, pre_room, ref2, ref3, replay_id, room; var available_logs, check_buffer_indentity, create_room_with_action, len2, len3, m, n, name, pre_room, ref2, ref3, replay_id, room;
//log.info info //log.info info
info.pass = info.pass.trim(); info.pass = info.pass.trim();
client.pass = info.pass; client.pass = info.pass;
...@@ -2573,6 +2630,26 @@ ...@@ -2573,6 +2630,26 @@
}); });
CLIENT_kick(client); CLIENT_kick(client);
}), 500); }), 500);
} else if (info.pass.toUpperCase() === "RC" && settings.modules.tournament_mode.enable_recover) {
ygopro.stoc_send_chat(client, "${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
available_logs = duel_log.duel_log.filter(function(duel) {
return duel.roommode !== 2 && _.any(duel.players, function(player) {
return player.real_name === client.name_vpass;
});
}).slice(0, 8);
_.each(available_logs, function(duel) {
var player_names;
player_names = duel.players[0].real_name + (duel.players[2] ? "+" + duel.players[2].real_name : "") + " VS " + (duel.players[1] ? duel.players[1].real_name : "AI") + (duel.players[3] ? "+" + duel.players[3].real_name : "");
return ygopro.stoc_send_chat(client, `<${duel.id}> ${player_names} ${duel.time}`, ygopro.constants.COLORS.BABYBLUE);
});
// 强行等待异步执行完毕_(:з」∠)_
setTimeout((function() {
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 1,
code: 9
});
CLIENT_kick(client);
}), 500);
} else if (info.pass.slice(0, 2).toUpperCase() === "R#" && settings.modules.cloud_replay.enabled) { } else if (info.pass.slice(0, 2).toUpperCase() === "R#" && settings.modules.cloud_replay.enabled) {
replay_id = info.pass.split("#")[1]; replay_id = info.pass.split("#")[1];
if (replay_id > 0 && replay_id <= 9) { if (replay_id > 0 && replay_id <= 9) {
...@@ -3201,6 +3278,11 @@ ...@@ -3201,6 +3278,11 @@
return; return;
} }
msg = buffer.readInt8(0); msg = buffer.readInt8(0);
//console.log client.pos, "MSG", ygopro.constants.MSG[msg]
if (ygopro.constants.MSG[msg] === 'RETRY' && room.recovering) {
room.finish_recover(true);
return true;
}
if (settings.modules.retry_handle.enabled) { if (settings.modules.retry_handle.enabled) {
if (ygopro.constants.MSG[msg] === 'RETRY') { if (ygopro.constants.MSG[msg] === 'RETRY') {
if (client.retry_count == null) { if (client.retry_count == null) {
...@@ -3238,8 +3320,16 @@ ...@@ -3238,8 +3320,16 @@
} }
// log.info(client.name, client.last_game_msg_title) // log.info(client.name, client.last_game_msg_title)
if ((msg >= 10 && msg < 30) || msg === 132 || (msg >= 140 && msg <= 144)) { //SELECT和ANNOUNCE开头的消息 if ((msg >= 10 && msg < 30) || msg === 132 || (msg >= 140 && msg <= 144)) { //SELECT和ANNOUNCE开头的消息
room.waiting_for_player = client; if (room.recovering) {
room.last_active_time = moment(); ygopro.ctos_send(server, 'RESPONSE', room.recover_replay.responses.splice(0, 1)[0]);
if (!room.recover_replay.responses.length) {
room.finish_recover();
}
return true;
} else {
room.waiting_for_player = client;
room.last_active_time = moment();
}
} }
//log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}") //log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}")
...@@ -3262,6 +3352,9 @@ ...@@ -3262,6 +3352,9 @@
ygopro.stoc_send_chat_to_room(room, "${death_start_extra}", ygopro.constants.COLORS.BABYBLUE); ygopro.stoc_send_chat_to_room(room, "${death_start_extra}", ygopro.constants.COLORS.BABYBLUE);
} }
} }
if (room.recovering) {
ygopro.stoc_send_chat_to_room(room, "${recover_start_hint}", ygopro.constants.COLORS.BABYBLUE);
}
} }
if (client.is_first && (room.hostinfo.mode !== 2 || client.pos === 0 || client.pos === 2)) { if (client.is_first && (room.hostinfo.mode !== 2 || client.pos === 0 || client.pos === 2)) {
room.first_list.push(client.name_vpass); room.first_list.push(client.name_vpass);
...@@ -3281,6 +3374,9 @@ ...@@ -3281,6 +3374,9 @@
if (ygopro.constants.MSG[msg] === 'NEW_TURN') { if (ygopro.constants.MSG[msg] === 'NEW_TURN') {
if (client.pos === 0) { if (client.pos === 0) {
room.turn++; room.turn++;
if (room.recovering && room.recover_from_turn <= room.turn) {
room.finish_recover();
}
if (room.death && room.death !== -2) { if (room.death && room.death !== -2) {
if (room.turn >= room.death) { if (room.turn >= room.death) {
oppo_pos = room.hostinfo.mode === 2 ? 2 : 1; oppo_pos = room.hostinfo.mode === 2 ? 2 : 1;
...@@ -3335,6 +3431,10 @@ ...@@ -3335,6 +3431,10 @@
} }
} }
if (ygopro.constants.MSG[msg] === 'WIN' && client.pos === 0) { if (ygopro.constants.MSG[msg] === 'WIN' && client.pos === 0) {
if (room.recovering) {
room.finish_recover(true);
return true;
}
pos = buffer.readUInt8(1); pos = buffer.readUInt8(1);
if (!(client.is_first || pos === 2 || room.duel_stage !== ygopro.constants.DUEL_STAGE.DUELING)) { if (!(client.is_first || pos === 2 || room.duel_stage !== ygopro.constants.DUEL_STAGE.DUELING)) {
pos = 1 - pos; pos = 1 - pos;
...@@ -3536,7 +3636,7 @@ ...@@ -3536,7 +3636,7 @@
} }
} }
//登场台词 //登场台词
if (settings.modules.dialogues.enabled) { if (settings.modules.dialogues.enabled && !room.recovering) {
if (ygopro.constants.MSG[msg] === 'SUMMONING' || ygopro.constants.MSG[msg] === 'SPSUMMONING' || ygopro.constants.MSG[msg] === 'CHAINING') { if (ygopro.constants.MSG[msg] === 'SUMMONING' || ygopro.constants.MSG[msg] === 'SPSUMMONING' || ygopro.constants.MSG[msg] === 'CHAINING') {
card = buffer.readUInt32LE(1); card = buffer.readUInt32LE(1);
trigger_location = buffer.readUInt8(6); trigger_location = buffer.readUInt8(6);
...@@ -3557,6 +3657,12 @@ ...@@ -3557,6 +3657,12 @@
client.ready_trap = false; client.ready_trap = false;
} }
} }
if (room.recovering && client.pos < 4) {
if (ygopro.constants.MSG[msg] !== 'WAITING') {
room.recover_buffers[client.pos].push(buffer);
}
return true;
}
return false; return false;
}); });
...@@ -4230,7 +4336,7 @@ ...@@ -4230,7 +4336,7 @@
}); });
ygopro.ctos_follow('UPDATE_DECK', true, function(buffer, info, client, server, datas) { ygopro.ctos_follow('UPDATE_DECK', true, function(buffer, info, client, server, datas) {
var buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, len2, len3, line, m, n, oppo_pos, room, struct, win_pos; var buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, len2, len3, line, m, n, oppo_pos, recover_player_data, room, struct, win_pos;
if (settings.modules.reconnect.enabled && client.pre_reconnecting) { if (settings.modules.reconnect.enabled && client.pre_reconnecting) {
if (!CLIENT_is_able_to_reconnect(client) && !CLIENT_is_able_to_kick_reconnect(client)) { if (!CLIENT_is_able_to_reconnect(client) && !CLIENT_is_able_to_kick_reconnect(client)) {
ygopro.stoc_send_chat(client, "${reconnect_failed}", ygopro.constants.COLORS.RED); ygopro.stoc_send_chat(client, "${reconnect_failed}", ygopro.constants.COLORS.RED);
...@@ -4300,14 +4406,29 @@ ...@@ -4300,14 +4406,29 @@
} }
return true; return true;
} }
struct = ygopro.structs["deck"];
struct._setBuff(buffer);
if (room.random_type || room.arena) { if (room.random_type || room.arena) {
if (client.pos === 0) { if (client.pos === 0) {
room.waiting_for_player = room.waiting_for_player2; room.waiting_for_player = room.waiting_for_player2;
} }
room.last_active_time = moment(); room.last_active_time = moment();
}
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.recovering) {
recover_player_data = _.find(room.recover_duel_log.players, function(player) {
return player.real_name === client.name_vpass;
});
if (recover_player_data && _.isEqual(buffer, Buffer.from(recover_player_data.deckbuf, "base64"))) {
if (recover_player_data.is_first) {
room.determine_firstgo = client;
}
} else {
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED);
}
} else if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.hostinfo.mode === 1 && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.deck_check && fs.readdirSync(settings.modules.tournament_mode.deck_path).length) { } else if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.hostinfo.mode === 1 && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.deck_check && fs.readdirSync(settings.modules.tournament_mode.deck_path).length) {
struct = ygopro.structs["deck"];
struct._setBuff(buffer);
struct.set("mainc", 1); struct.set("mainc", 1);
struct.set("sidec", 1); struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]); struct.set("deckbuf", [4392470, 4392470]);
...@@ -4373,17 +4494,6 @@ ...@@ -4373,17 +4494,6 @@
if (!room) { if (!room) {
return; return;
} }
if (settings.modules.reconnect.enabled) {
if (client.closed) {
ygopro.ctos_send(server, 'TIME_CONFIRM');
return true;
} else {
client.time_confirm_required = true;
}
}
if (!(settings.modules.heartbeat_detection.enabled && room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING && !room.windbot)) {
return;
}
check = false; check = false;
if (room.hostinfo.mode !== 2) { if (room.hostinfo.mode !== 2) {
check = (client.is_first && info.player === 0) || (!client.is_first && info.player === 1); check = (client.is_first && info.player === 0) || (!client.is_first && info.player === 1);
...@@ -4412,6 +4522,23 @@ ...@@ -4412,6 +4522,23 @@
} }
check = client.pos === cur_players[info.player]; check = client.pos === cur_players[info.player];
} }
if (room.recovering) {
if (check) {
ygopro.ctos_send(server, 'TIME_CONFIRM');
}
return true;
}
if (settings.modules.reconnect.enabled) {
if (client.closed) {
ygopro.ctos_send(server, 'TIME_CONFIRM');
return true;
} else {
client.time_confirm_required = true;
}
}
if (!(settings.modules.heartbeat_detection.enabled && room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING && !room.windbot)) {
return;
}
if (check) { if (check) {
CLIENT_heartbeat_register(client, false); CLIENT_heartbeat_register(client, false);
} }
...@@ -4424,6 +4551,9 @@ ...@@ -4424,6 +4551,9 @@
if (!room) { if (!room) {
return; return;
} }
if (room.recovered) {
room.recovered = false;
}
if (settings.modules.reconnect.enabled) { if (settings.modules.reconnect.enabled) {
if (client.waiting_for_last) { if (client.waiting_for_last) {
client.waiting_for_last = false; client.waiting_for_last = false;
...@@ -4450,13 +4580,12 @@ ...@@ -4450,13 +4580,12 @@
return; return;
} }
client.selected_preduel = true; client.selected_preduel = true;
if (!(room.random_type || room.arena)) { if (room.random_type || room.arena) {
return; if (client.pos === 0) {
} room.waiting_for_player = room.waiting_for_player2;
if (client.pos === 0) { }
room.waiting_for_player = room.waiting_for_player2; room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's');
} }
room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's');
}); });
ygopro.ctos_follow('TP_RESULT', false, function(buffer, info, client, server, datas) { ygopro.ctos_follow('TP_RESULT', false, function(buffer, info, client, server, datas) {
...@@ -4512,40 +4641,64 @@ ...@@ -4512,40 +4641,64 @@
} }
}); });
ygopro.stoc_follow('SELECT_HAND', false, function(buffer, info, client, server, datas) { ygopro.stoc_follow('SELECT_HAND', true, function(buffer, info, client, server, datas) {
var room; var room;
room = ROOM_all[client.rid]; room = ROOM_all[client.rid];
if (!room) { if (!room) {
return; return false;
} }
client.selected_preduel = false;
if (client.pos === 0) { if (client.pos === 0) {
room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER; room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER;
} }
if (!(room.random_type || room.arena)) { if (room.random_type || room.arena) {
return; if (client.pos === 0) {
room.waiting_for_player = client;
} else {
room.waiting_for_player2 = client;
}
room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's');
} }
if (client.pos === 0) { if (room.determine_firstgo) {
room.waiting_for_player = client; ygopro.ctos_send(server, "HAND_RESULT", {
res: client.pos === 0 ? 2 : 1
});
return true;
} else { } else {
room.waiting_for_player2 = client; client.selected_preduel = false;
} }
room.last_active_time = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's'); return false;
}); });
ygopro.stoc_follow('SELECT_TP', false, function(buffer, info, client, server, datas) { ygopro.stoc_follow('HAND_RESULT', true, function(buffer, info, client, server, datas) {
var room; var room;
room = ROOM_all[client.rid]; room = ROOM_all[client.rid];
if (!room) { if (!room) {
return; return false;
}
return room.determine_firstgo;
});
ygopro.stoc_follow('SELECT_TP', true, function(buffer, info, client, server, datas) {
var room;
room = ROOM_all[client.rid];
if (!room) {
return false;
} }
client.selected_preduel = false;
room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO; room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO;
room.selecting_tp = client;
if (room.random_type || room.arena) { if (room.random_type || room.arena) {
room.waiting_for_player = client; room.waiting_for_player = client;
room.last_active_time = moment(); room.last_active_time = moment();
} }
if (room.determine_firstgo) {
ygopro.ctos_send(server, "TP_RESULT", {
res: room.determine_firstgo === client ? 1 : 0
});
return true;
} else {
client.selected_preduel = false;
room.selecting_tp = client;
}
return false;
}); });
ygopro.stoc_follow('CHANGE_SIDE', false, function(buffer, info, client, server, datas) { ygopro.stoc_follow('CHANGE_SIDE', false, function(buffer, info, client, server, datas) {
...@@ -4653,9 +4806,10 @@ ...@@ -4653,9 +4806,10 @@
for (o = 0, len4 = ref4.length; o < len4; o++) { for (o = 0, len4 = ref4.length; o < len4; o++) {
player = ref4[o]; player = ref4[o];
results.push({ results.push({
real_name: player.name, real_name: player.name_vpass,
deckbuf: player.deckbuf.toString("base64"), deckbuf: player.start_deckbuf.toString("base64"),
pos: player.pos, pos: player.pos,
is_first: player.is_first,
name: player.name + (settings.modules.tournament_mode.show_ip && !player.is_local ? " (IP: " + player.ip.slice(7) + ")" : "") + (settings.modules.tournament_mode.show_info && !(room.hostinfo.mode === 2 && player.pos % 2 > 0) ? " (Score:" + room.scores[player.name_vpass] + " LP:" + (player.lp != null ? player.lp : room.hostinfo.start_lp) + (room.hostinfo.mode !== 2 ? " Cards:" + (player.card_count != null ? player.card_count : room.hostinfo.start_hand) : "") + ")" : ""), name: player.name + (settings.modules.tournament_mode.show_ip && !player.is_local ? " (IP: " + player.ip.slice(7) + ")" : "") + (settings.modules.tournament_mode.show_info && !(room.hostinfo.mode === 2 && player.pos % 2 > 0) ? " (Score:" + room.scores[player.name_vpass] + " LP:" + (player.lp != null ? player.lp : room.hostinfo.start_lp) + (room.hostinfo.mode !== 2 ? " Cards:" + (player.card_count != null ? player.card_count : room.hostinfo.start_hand) : "") + ")" : ""),
winner: player.pos === room.winner winner: player.pos === room.winner
}); });
...@@ -4684,7 +4838,7 @@ ...@@ -4684,7 +4838,7 @@
setInterval(function() { setInterval(function() {
_async.each(ROOM_all, function(room, done) { _async.each(ROOM_all, function(room, done) {
var time_passed; var time_passed;
if (!(room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && room.random_type && room.last_active_time && room.waiting_for_player && room.get_disconnected_count() === 0 && (!settings.modules.side_timeout || room.duel_stage !== ygopro.constants.DUEL_STAGE.SIDING))) { if (!(room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && room.random_type && room.last_active_time && room.waiting_for_player && room.get_disconnected_count() === 0 && (!settings.modules.side_timeout || room.duel_stage !== ygopro.constants.DUEL_STAGE.SIDING) && !room.recovered)) {
done(); done();
return; return;
} }
...@@ -4711,7 +4865,7 @@ ...@@ -4711,7 +4865,7 @@
setInterval(function() { setInterval(function() {
_async.each(ROOM_all, function(room, done) { _async.each(ROOM_all, function(room, done) {
var time_passed; var time_passed;
if (!(room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && room.arena && room.last_active_time && room.waiting_for_player && room.get_disconnected_count() === 0 && (!settings.modules.side_timeout || room.duel_stage !== ygopro.constants.DUEL_STAGE.SIDING))) { if (!(room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && room.arena && room.last_active_time && room.waiting_for_player && room.get_disconnected_count() === 0 && (!settings.modules.side_timeout || room.duel_stage !== ygopro.constants.DUEL_STAGE.SIDING) && !room.recovered)) {
done(); done();
return; return;
} }
......
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