Commit bf04c06b authored by nanahira's avatar nanahira

Merge branch 'master' into ai-play

parents 707e22ea 33727617
......@@ -22,7 +22,7 @@ class Challonge {
return this.previous;
}
try {
const { data: { tournament } } = await axios_1.default.get(`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}.json`, {
const { data: { tournament } } = await axios_1.default.get(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}.json`, {
params: {
api_key: this.config.api_key,
include_participants: 1,
......@@ -35,7 +35,7 @@ class Challonge {
return tournament;
}
catch (e) {
this.log.error(`Failed to get tournament ${this.config.tournament_id}`, e);
this.log.error(`Failed to get tournament ${this.config.tournament_id}: ${e}`);
return;
}
}
......@@ -47,7 +47,7 @@ class Challonge {
}
async putScore(matchId, match, retried = 0) {
try {
await axios_1.default.put(`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`, {
await axios_1.default.put(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`, {
api_key: this.config.api_key,
match: match,
});
......@@ -56,7 +56,7 @@ class Challonge {
return true;
}
catch (e) {
this.log.error(`Failed to put score for match ${matchId}`, e);
this.log.error(`Failed to put score for match ${matchId}: ${e}`);
if (retried < 5) {
this.log.info(`Retrying match ${matchId}`);
return this.putScore(matchId, match, retried + 1);
......@@ -67,6 +67,34 @@ class Challonge {
}
}
}
async clearParticipants() {
try {
await axios_1.default.delete(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/clear.json`, {
params: {
api_key: this.config.api_key
},
validateStatus: () => true,
});
return true;
}
catch (e) {
this.log.error(`Failed to clear participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
async uploadParticipants(participantNames) {
try {
await axios_1.default.post(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/bulk_add.json`, {
api_key: this.config.api_key,
participants: participantNames.map(name => ({ name })),
});
return true;
}
catch (e) {
this.log.error(`Failed to upload participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
}
exports.Challonge = Challonge;
//# sourceMappingURL=challonge.js.map
\ No newline at end of file
......@@ -2,34 +2,15 @@ import axios from 'axios';
import { createLogger } from 'bunyan';
import moment, { Moment } from 'moment';
import PQueue from 'p-queue';
import _ from 'underscore';
export interface Match {
attachment_count?: any;
created_at: string;
group_id?: any;
has_attachment: boolean;
id: number;
identifier: string;
location?: any;
loser_id?: any;
state: 'pending' | 'open' | 'complete'; // pending: 还未开始,open: 进行中,complete: 已结束
player1_id: number;
player1_is_prereq_match_loser: boolean;
player1_prereq_match_id?: any;
player1_votes?: any;
player2_id: number;
player2_is_prereq_match_loser: boolean;
player2_prereq_match_id?: any;
player2_votes?: any;
round: number;
scheduled_time?: any;
started_at: string;
state: string;
tournament_id: number;
underway_at?: any;
updated_at: string;
winner_id?: any;
prerequisite_match_ids_csv: string;
scores_csv: string;
winner_id?: number | 'tie'; // 如果存在,则代表该比赛已经结束
scores_csv?: string; // 比分,2-1 这样的格式,请保证和上传的情况相同
}
export interface MatchWrapper {
......@@ -37,34 +18,9 @@ export interface MatchWrapper {
}
export interface Participant {
active: boolean;
checked_in_at?: any;
created_at: string;
final_rank?: any;
group_id?: any;
icon?: any;
id: number;
invitation_id?: any;
invite_email?: any;
misc?: any;
name: string;
on_waiting_list: boolean;
seed: number;
tournament_id: number;
updated_at: string;
challonge_username?: any;
challonge_email_address_verified?: any;
removable: boolean;
participatable_or_invitation_attached: boolean;
confirm_remove: boolean;
invitation_pending: boolean;
display_name_with_invitation_email_address: string;
email_hash?: any;
username?: any;
attached_participatable_portrait_url?: any;
can_check_in: boolean;
checked_in: boolean;
reactivatable: boolean;
deckbuf?: string;
}
export interface ParticipantWrapper {
......@@ -72,86 +28,27 @@ export interface ParticipantWrapper {
}
export interface Tournament {
accept_attachments: boolean;
allow_participant_match_reporting: boolean;
anonymous_voting: boolean;
category?: any;
check_in_duration?: any;
completed_at?: any;
created_at: string;
created_by_api: boolean;
credit_capped: boolean;
description: string;
game_id: number;
group_stages_enabled: boolean;
hide_forum: boolean;
hide_seeds: boolean;
hold_third_place_match: boolean;
id: number;
max_predictions_per_user: number;
name: string;
notify_users_when_matches_open: boolean;
notify_users_when_the_tournament_ends: boolean;
open_signup: boolean;
participants_count: number;
prediction_method: number;
predictions_opened_at?: any;
private: boolean;
progress_meter: number;
pts_for_bye: string;
pts_for_game_tie: string;
pts_for_game_win: string;
pts_for_match_tie: string;
pts_for_match_win: string;
quick_advance: boolean;
ranked_by: string;
require_score_agreement: boolean;
rr_pts_for_game_tie: string;
rr_pts_for_game_win: string;
rr_pts_for_match_tie: string;
rr_pts_for_match_win: string;
sequential_pairings: boolean;
show_rounds: boolean;
signup_cap?: any;
start_at?: any;
started_at: string;
started_checking_in_at?: any;
state: string;
swiss_rounds: number;
teams: boolean;
tie_breaks: string[];
tournament_type: string;
updated_at: string;
url: string;
description_source: string;
subdomain?: any;
full_challonge_url: string;
live_image_url: string;
sign_up_url?: any;
review_before_finalizing: boolean;
accepting_predictions: boolean;
participants_locked: boolean;
game_name: string;
participants_swappable: boolean;
team_convertable: boolean;
group_stages_were_started: boolean;
participants: ParticipantWrapper[];
matches: MatchWrapper[];
}
// GET /v1/tournaments/${tournament_id}.json?api_key=xxx&include_participants=1&include_matches=1 returns { tournament: Tournament }
export interface TournamentWrapper {
tournament: Tournament;
}
// PUT /v1/tournaments/${tournament_id}/matches/${match_id}.json { api_key: string, match: MatchPost } returns ANY
export interface MatchPost {
scores_csv: string;
winner_id: number;
scores_csv: string; // 比分。2-1 这样的格式。可能有特殊情况,比如 -9-1 或者 1--9,代表有一方掉线,或是加时赛胜利。也就是允许负数(从第一串数字的最后一个 - 区分)
winner_id?: number | 'tie'; // 上传比分的时候这个字段不一定存在。如果不存在的话代表比赛没打完(比如 1-0 就会上传,这时候换 side)
}
export interface ChallongeConfig {
api_key: string;
tournament_id: string;
cache_ttl: number;
challonge_url: string;
}
export class Challonge {
......@@ -169,7 +66,7 @@ export class Challonge {
}
try {
const { data: { tournament } } = await axios.get<TournamentWrapper>(
`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}.json`,
`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}.json`,
{
params: {
api_key: this.config.api_key,
......@@ -183,7 +80,7 @@ export class Challonge {
this.previousTime = moment();
return tournament;
} catch (e) {
this.log.error(`Failed to get tournament ${this.config.tournament_id}`, e);
this.log.error(`Failed to get tournament ${this.config.tournament_id}: ${e}`);
return;
}
}
......@@ -198,7 +95,7 @@ export class Challonge {
async putScore(matchId: number, match: MatchPost, retried = 0) {
try {
await axios.put(
`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`,
`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`,
{
api_key: this.config.api_key,
match: match,
......@@ -208,7 +105,7 @@ export class Challonge {
this.previousTime = undefined;
return true;
} catch (e) {
this.log.error(`Failed to put score for match ${matchId}`, e);
this.log.error(`Failed to put score for match ${matchId}: ${e}`);
if (retried < 5) {
this.log.info(`Retrying match ${matchId}`);
return this.putScore(matchId, match, retried + 1);
......@@ -218,4 +115,34 @@ export class Challonge {
}
}
}
// DELETE /v1/tournaments/${tournament_id}/participants/clear.json?api_key=xxx returns ANY
async clearParticipants() {
try {
await axios.delete(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/clear.json`, {
params: {
api_key: this.config.api_key
},
validateStatus: () => true,
})
return true;
} catch (e) {
this.log.error(`Failed to clear participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
// POST /v1/tournaments/${tournament_id}/participants/bulk_add.json { api_key: string, participants: { name: string }[] } returns ANY
async uploadParticipants(participantNames: string[]) {
try {
await axios.post(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/bulk_add.json`, {
api_key: this.config.api_key,
participants: participantNames.map(name => ({ name })),
});
return true;
} catch (e) {
this.log.error(`Failed to upload participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
}
......@@ -166,7 +166,8 @@
"cache_ttl": 60000,
"no_match_mode": false,
"api_key": "123",
"tournament_id": "456"
"tournament_id": "456",
"challonge_url": "https://api.challonge.com"
},
"deck_log": {
"enabled": false,
......
......@@ -1101,13 +1101,13 @@ CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = (client, deck
if !room
CLIENT_reconnect_unregister(client)
return false
if deckbuf and deckbuf.compare(disconnect_info.deckbuf) != 0
if deckbuf and not deckbuf.equals(disconnect_info.deckbuf)
return false
return true
CLIENT_get_kick_reconnect_target = global.CLIENT_get_kick_reconnect_target = (client, deckbuf) ->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and !room.windbot
for player in room.get_playing_player() when !player.isClosed and player.name == client.name and (settings.modules.challonge.enabled or player.pass == client.pass) and (settings.modules.mycard.enabled or settings.modules.tournament_mode.enabled or player.ip == client.ip or (client.vpass and client.vpass == player.vpass)) and (!deckbuf or deckbuf.compare(player.start_deckbuf) == 0)
for player in room.get_playing_player() when !player.isClosed and player.name == client.name and (settings.modules.challonge.enabled or player.pass == client.pass) and (settings.modules.mycard.enabled or settings.modules.tournament_mode.enabled or player.ip == client.ip or (client.vpass and client.vpass == player.vpass)) and (!deckbuf or deckbuf.equals(player.start_deckbuf))
return player
return null
......@@ -3698,6 +3698,25 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
return true
struct = ygopro.structs.get("deck")
struct._setBuff(buffer)
deck_ok = (msg) ->
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.BABYBLUE)
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and client.deck_good and !client.is_local and !client.bot_bound
client.bot_bound = true
ygopro.stoc_send_chat(client, "${stand_bot_added}", ygopro.constants.COLORS.BABYBLUE)
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
})
ygopro.ctos_send(server, 'HS_TOOBSERVER')
room.add_windbot_stand(client.name_vpass, buffer)
return true
return false
deck_bad = (msg) ->
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.RED)
client.deck_good = false
return false
if room.random_type or room.arena
if client.pos == 0
room.waiting_for_player = room.waiting_for_player2
......@@ -3705,7 +3724,7 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
client.deck_good = true
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering
recover_player_data = _.find(room.recover_duel_log.players, (player) ->
return player.realName == client.name_vpass and buffer.compare(Buffer.from(player.startDeckBuffer, "base64")) == 0
return player.realName == client.name_vpass and buffer.equals(Buffer.from(player.startDeckBuffer, "base64"))
)
if recover_player_data
recoveredDeck = recover_player_data.getCurrentDeck()
......@@ -3715,39 +3734,39 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
if recover_player_data.isFirst
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)
client.deck_good = false
return false
else
return deck_bad("${deck_incorrect_reconnect}")
else if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
if room.arena and settings.modules.athletic_check.enabled and settings.modules.athletic_check.banCount
athleticCheckResult = await athleticChecker.checkAthletic({main: buff_main, side: buff_side})
if athleticCheckResult.success
if athleticCheckResult.athletic and athleticCheckResult.athletic <= settings.modules.athletic_check.banCount
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
ygopro.stoc_send_chat(client, "${banned_athletic_deck_part1}#{settings.modules.athletic_check.banCount}${banned_athletic_deck_part2}", ygopro.constants.COLORS.RED)
client.deck_good = false
return false
return deck_bad("${banned_athletic_deck_part1}#{settings.modules.athletic_check.banCount}${banned_athletic_deck_part2}")
else
log.warn("GET ATHLETIC FAIL", client.name, athleticCheckResult.message)
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.deck_check and !client.is_local
if settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.deck_check
if settings.modules.challonge.enabled and client.challonge_info and client.challonge_info.deckbuf
trim_deckbuf = (buf) ->
mainc = buf.readUInt32LE(0)
sidec = buf.readUInt32LE(4)
# take first (2 + mainc + sidec) * 4 bytes
return buf.slice(0, (2 + mainc + sidec) * 4)
deckbuf_from_challonge = Buffer.from(client.challonge_info.deckbuf, "base64")
if trim_deckbuf(deckbuf_from_challonge).equals(trim_deckbuf(buffer))
#log.info("deck ok: " + client.name)
return deck_ok("${deck_correct_part1} #{client.challonge_info.name} ${deck_correct_part2}")
else
#log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
return deck_bad("${deck_incorrect_part1} #{client.challonge_info.name} ${deck_incorrect_part2}")
else
decks = await fs.promises.readdir(settings.modules.tournament_mode.deck_path)
if decks.length
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
buffer = struct.buffer
found_deck=false
for deck in decks
if deck_name_match(deck, client.name)
found_deck=deck
if found_deck
deck_text = await fs.promises.readFile(settings.modules.tournament_mode.deck_path+found_deck,{encoding:"ASCII"})
deck_array=deck_text.split("\n")
deck_array=deck_text.split(/\r?\n/)
deck_main=[]
deck_side=[]
current_deck=deck_main
......@@ -3755,34 +3774,16 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
if line.indexOf("!side")>=0
current_deck=deck_side
card=parseInt(line)
current_deck.push(card) unless isNaN(card)
current_deck.push(card) unless isNaN(card) or line.endsWith("#")
if _.isEqual(buff_main, deck_main) and _.isEqual(buff_side, deck_side)
deckbuf=deck_main.concat(deck_side)
struct.set("mainc", deck_main.length)
struct.set("sidec", deck_side.length)
struct.set("deckbuf", deckbuf)
buffer = struct.buffer
#log.info("deck ok: " + client.name)
ygopro.stoc_send_chat(client, "${deck_correct_part1} #{found_deck} ${deck_correct_part2}", ygopro.constants.COLORS.BABYBLUE)
return deck_ok("${deck_correct_part1} #{found_deck} ${deck_correct_part2}")
else
#log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
ygopro.stoc_send_chat(client, "${deck_incorrect_part1} #{found_deck} ${deck_incorrect_part2}", ygopro.constants.COLORS.RED)
client.deck_good = false
return false
return deck_bad("${deck_incorrect_part1} #{found_deck} ${deck_incorrect_part2}")
else
#log.info("player deck not found: " + client.name)
ygopro.stoc_send_chat(client, "#{client.name}${deck_not_found}", ygopro.constants.COLORS.RED)
client.deck_good = false
return false
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and client.deck_good and !client.is_local and !client.bot_bound
client.bot_bound = true
ygopro.stoc_send_chat(client, "${stand_bot_added}", ygopro.constants.COLORS.BABYBLUE)
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
})
ygopro.ctos_send(server, 'HS_TOOBSERVER')
room.add_windbot_stand(client.name_vpass, buffer)
return true
return deck_bad("#{client.name}${deck_not_found}")
await return false
ygopro.ctos_follow 'RESPONSE', false, (buffer, info, client, server, datas)->
......
......@@ -1451,7 +1451,7 @@
CLIENT_reconnect_unregister(client);
return false;
}
if (deckbuf && deckbuf.compare(disconnect_info.deckbuf) !== 0) {
if (deckbuf && !deckbuf.equals(disconnect_info.deckbuf)) {
return false;
}
return true;
......@@ -1465,7 +1465,7 @@
ref = room.get_playing_player();
for (l = 0, len1 = ref.length; l < len1; l++) {
player = ref[l];
if (!player.isClosed && player.name === client.name && (settings.modules.challonge.enabled || player.pass === client.pass) && (settings.modules.mycard.enabled || settings.modules.tournament_mode.enabled || player.ip === client.ip || (client.vpass && client.vpass === player.vpass)) && (!deckbuf || deckbuf.compare(player.start_deckbuf) === 0)) {
if (!player.isClosed && player.name === client.name && (settings.modules.challonge.enabled || player.pass === client.pass) && (settings.modules.mycard.enabled || settings.modules.tournament_mode.enabled || player.ip === client.ip || (client.vpass && client.vpass === player.vpass)) && (!deckbuf || deckbuf.equals(player.start_deckbuf))) {
return player;
}
}
......@@ -4891,7 +4891,7 @@
});
ygopro.ctos_follow('UPDATE_DECK', true, async function(buffer, info, client, server, datas) {
var athleticCheckResult, buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, j, l, len, len1, line, oppo_pos, recover_player_data, recoveredDeck, room, struct, win_pos;
var athleticCheckResult, buff_main, buff_side, card, current_deck, deck, deck_array, deck_bad, deck_main, deck_ok, deck_side, deck_text, deckbuf_from_challonge, decks, found_deck, i, j, l, len, len1, line, oppo_pos, recover_player_data, recoveredDeck, room, struct, trim_deckbuf, win_pos;
if (settings.modules.reconnect.enabled && client.pre_reconnecting) {
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);
......@@ -4963,6 +4963,28 @@
}
struct = ygopro.structs.get("deck");
struct._setBuff(buffer);
deck_ok = function(msg) {
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.BABYBLUE);
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && client.deck_good && !client.is_local && !client.bot_bound) {
client.bot_bound = true;
ygopro.stoc_send_chat(client, "${stand_bot_added}", ygopro.constants.COLORS.BABYBLUE);
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
});
ygopro.ctos_send(server, 'HS_TOOBSERVER');
room.add_windbot_stand(client.name_vpass, buffer);
return true;
}
return false;
};
deck_bad = function(msg) {
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.RED);
client.deck_good = false;
return false;
};
if (room.random_type || room.arena) {
if (client.pos === 0) {
room.waiting_for_player = room.waiting_for_player2;
......@@ -4972,7 +4994,7 @@
client.deck_good = true;
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.recovering) {
recover_player_data = _.find(room.recover_duel_log.players, function(player) {
return player.realName === client.name_vpass && buffer.compare(Buffer.from(player.startDeckBuffer, "base64")) === 0;
return player.realName === client.name_vpass && buffer.equals(Buffer.from(player.startDeckBuffer, "base64"));
});
if (recover_player_data) {
recoveredDeck = recover_player_data.getCurrentDeck();
......@@ -4983,14 +5005,9 @@
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);
client.deck_good = false;
return false;
return deck_bad("${deck_incorrect_reconnect}");
}
} else {
} else if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
if (room.arena && settings.modules.athletic_check.enabled && settings.modules.athletic_check.banCount) {
athleticCheckResult = (await athleticChecker.checkAthletic({
main: buff_main,
......@@ -4998,24 +5015,32 @@
}));
if (athleticCheckResult.success) {
if (athleticCheckResult.athletic && athleticCheckResult.athletic <= settings.modules.athletic_check.banCount) {
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
ygopro.stoc_send_chat(client, `\${banned_athletic_deck_part1}${settings.modules.athletic_check.banCount}\${banned_athletic_deck_part2}`, ygopro.constants.COLORS.RED);
client.deck_good = false;
return false;
return deck_bad(`\${banned_athletic_deck_part1}${settings.modules.athletic_check.banCount}\${banned_athletic_deck_part2}`);
}
} else {
log.warn("GET ATHLETIC FAIL", client.name, athleticCheckResult.message);
}
}
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.deck_check && !client.is_local) {
if (settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.deck_check) {
if (settings.modules.challonge.enabled && client.challonge_info && client.challonge_info.deckbuf) {
trim_deckbuf = function(buf) {
var mainc, sidec;
mainc = buf.readUInt32LE(0);
sidec = buf.readUInt32LE(4);
// take first (2 + mainc + sidec) * 4 bytes
return buf.slice(0, (2 + mainc + sidec) * 4);
};
deckbuf_from_challonge = Buffer.from(client.challonge_info.deckbuf, "base64");
if (trim_deckbuf(deckbuf_from_challonge).equals(trim_deckbuf(buffer))) {
//log.info("deck ok: " + client.name)
return deck_ok(`\${deck_correct_part1} ${client.challonge_info.name} \${deck_correct_part2}`);
} else {
//log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
return deck_bad(`\${deck_incorrect_part1} ${client.challonge_info.name} \${deck_incorrect_part2}`);
}
} else {
decks = (await fs.promises.readdir(settings.modules.tournament_mode.deck_path));
if (decks.length) {
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
buffer = struct.buffer;
found_deck = false;
for (j = 0, len = decks.length; j < len; j++) {
deck = decks[j];
......@@ -5027,7 +5052,7 @@
deck_text = (await fs.promises.readFile(settings.modules.tournament_mode.deck_path + found_deck, {
encoding: "ASCII"
}));
deck_array = deck_text.split("\n");
deck_array = deck_text.split(/\r?\n/);
deck_main = [];
deck_side = [];
current_deck = deck_main;
......@@ -5037,42 +5062,24 @@
current_deck = deck_side;
}
card = parseInt(line);
if (!isNaN(card)) {
if (!(isNaN(card) || line.endsWith("#"))) {
current_deck.push(card);
}
}
if (_.isEqual(buff_main, deck_main) && _.isEqual(buff_side, deck_side)) {
deckbuf = deck_main.concat(deck_side);
struct.set("mainc", deck_main.length);
struct.set("sidec", deck_side.length);
struct.set("deckbuf", deckbuf);
buffer = struct.buffer;
//log.info("deck ok: " + client.name)
ygopro.stoc_send_chat(client, `\${deck_correct_part1} ${found_deck} \${deck_correct_part2}`, ygopro.constants.COLORS.BABYBLUE);
return deck_ok(`\${deck_correct_part1} ${found_deck} \${deck_correct_part2}`);
} else {
//log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
ygopro.stoc_send_chat(client, `\${deck_incorrect_part1} ${found_deck} \${deck_incorrect_part2}`, ygopro.constants.COLORS.RED);
client.deck_good = false;
return false;
return deck_bad(`\${deck_incorrect_part1} ${found_deck} \${deck_incorrect_part2}`);
}
} else {
//log.info("player deck not found: " + client.name)
ygopro.stoc_send_chat(client, `${client.name}\${deck_not_found}`, ygopro.constants.COLORS.RED);
client.deck_good = false;
return false;
return deck_bad(`${client.name}\${deck_not_found}`);
}
}
}
}
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && client.deck_good && !client.is_local && !client.bot_bound) {
client.bot_bound = true;
ygopro.stoc_send_chat(client, "${stand_bot_added}", ygopro.constants.COLORS.BABYBLUE);
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
});
ygopro.ctos_send(server, 'HS_TOOBSERVER');
room.add_windbot_stand(client.name_vpass, buffer);
return true;
}
return false;
});
......
......@@ -23,6 +23,8 @@ const auth = require('./ygopro-auth.js');
const settings = loadJSON('./config/config.json');
config = settings.modules.tournament_mode;
challonge_config = settings.modules.challonge;
const { Challonge } = require('./challonge');
const challonge = new Challonge(challonge_config);
ssl_config = settings.modules.http.ssl;
const _async = require("async");
......@@ -73,7 +75,7 @@ const readDeck = async function(deck_name, deck_full_path) {
const deck={};
deck.name=deck_name;
deck_text = await fs.promises.readFile(deck_full_path, { encoding: "ASCII" });
deck_array = deck_text.split("\n");
deck_array = deck_text.split(/\r?\n/);
deck.main = [];
deck.extra = [];
deck.side = [];
......@@ -87,7 +89,7 @@ const readDeck = async function(deck_name, deck_full_path) {
current_deck = deck.side;
}
card = parseInt(line);
if (!isNaN(card)) {
if (!isNaN(card) && !line.endsWith("#")) {
current_deck.push(card);
}
}
......@@ -156,19 +158,11 @@ const UploadToChallonge = async function () {
sendResponse("读取玩家列表完毕,共有" + player_list.length + "名玩家。");
try {
sendResponse("开始清空 Challonge 玩家列表。");
await axios.delete(`https://api.challonge.com/v1/tournaments/${challonge_config.tournament_id}/participants/clear.json`, {
params: {
api_key: challonge_config.api_key
},
validateStatus: () => true,
});
await challonge.clearParticipants();
sendResponse("开始上传玩家列表至 Challonge。");
for (const chunk of _.chunk(player_list, 10)) {
sendResponse(`开始上传玩家 ${chunk.join(', ')} 至 Challonge。`);
await axios.post(`https://api.challonge.com/v1/tournaments/${challonge_config.tournament_id}/participants/bulk_add.json`, {
api_key: challonge_config.api_key,
participants: chunk.map(name => ({ name })),
});
await challonge.uploadParticipants(chunk);
}
sendResponse("玩家列表上传完成。");
} catch (e) {
......
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