ygopro-server.js 202 KB
Newer Older
nanahira's avatar
nanahira committed
1
// Generated by CoffeeScript 2.5.1
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2
(function() {
nanahira's avatar
nanahira committed
3
  // 标准库
nanahira's avatar
nanahira committed
4
  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, Q, 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_oppentlist, ROOM_players_scores, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, athleticChecker, auth, axios, badwords, ban_user, bunyan, challonge, challonge_cache, challonge_queue_callbacks, checkFileExists, createDirectoryIfNotExists, crypto, dataManager, deck_name_match, dialogues, disconnect_list, exec, execFile, fs, geoip, getSeedTimet, get_callback, get_memory_usage, http, httpRequestListener, importOldConfig, import_datas, init, is_challonge_requesting, lflists, loadJSON, loadJSONAsync, loadLFList, loadRemoteData, load_dialogues, load_tips, log, long_resolve_cards, memory_usage, merge, moment, net, netRequestHandler, os, path, qs, real_windbot_server_ip, refresh_challonge_cache, release_disconnect, replaced_index, report_to_big_brother, request, 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, ygopro, zlib;
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
5

神楽坂玲奈's avatar
神楽坂玲奈 committed
6
  net = require('net');
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
7

神楽坂玲奈's avatar
神楽坂玲奈 committed
8 9 10
  http = require('http');

  url = require('url');
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
11

神楽坂玲奈's avatar
神楽坂玲奈 committed
12 13 14 15
  path = require('path');

  fs = require('fs');

mercury233's avatar
mercury233 committed
16 17
  os = require('os');

18 19
  crypto = require('crypto');

mercury233's avatar
mercury233 committed
20 21
  exec = require('child_process').exec;

神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
22 23
  execFile = require('child_process').execFile;

mercury233's avatar
mercury233 committed
24 25 26 27
  spawn = require('child_process').spawn;

  spawnSync = require('child_process').spawnSync;

nanahira's avatar
nanahira committed
28 29
  _async = require('async');

nanahira's avatar
nanahira committed
30
  // 三方库
nanahira's avatar
nanahira committed
31
  _ = global._ = require('underscore');
神楽坂玲奈's avatar
神楽坂玲奈 committed
32

神楽坂玲奈's avatar
tip  
神楽坂玲奈 committed
33 34 35 36 37 38
  _.str = require('underscore.string');

  _.mixin(_.str.exports());

  request = require('request');

nanahira's avatar
nanahira committed
39 40
  axios = require('axios');

nanahira's avatar
fix  
nanahira committed
41
  qs = require("querystring");
nanahira's avatar
nanahira committed
42

nanahira's avatar
nanahira committed
43 44
  zlib = require('zlib');

nanahira's avatar
nanahira committed
45 46
  axios = require('axios');

神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
47 48
  bunyan = require('bunyan');

nanahira's avatar
nanahira committed
49
  log = global.log = bunyan.createLogger({
mercury233's avatar
mercury233 committed
50 51 52
    name: "mycard"
  });

nanahira's avatar
nanahira committed
53
  moment = global.moment = require('moment');
54

mercury233's avatar
mercury233 committed
55
  moment.updateLocale('zh-cn', {
mercury233's avatar
mercury233 committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
    relativeTime: {
      future: '%s内',
      past: '%s前',
      s: '%d秒',
      m: '1分钟',
      mm: '%d分钟',
      h: '1小时',
      hh: '%d小时',
      d: '1天',
      dd: '%d天',
      M: '1个月',
      MM: '%d个月',
      y: '1年',
      yy: '%d年'
    }
  });

nanahira's avatar
nanahira committed
73
  import_datas = global.import_datas = ["abuse_count", "ban_mc", "vpass", "rag", "rid", "is_post_watcher", "retry_count", "name", "pass", "name_vpass", "is_first", "lp", "card_count", "is_host", "pos", "surrend_confirm", "kick_count", "deck_saved", "main", "side", "side_interval", "side_tcount", "selected_preduel", "last_game_msg", "last_game_msg_title", "last_hint_msg", "start_deckbuf", "challonge_info", "ready_trap", "join_time", "arena_quit_free", "replays_sent"];
nanahira's avatar
nanahira committed
74

mercury233's avatar
mercury233 committed
75 76
  merge = require('deepmerge');

mercury233's avatar
mercury233 committed
77 78
  loadJSON = require('load-json-file').sync;

nanahira's avatar
nanahira committed
79 80
  loadJSONAsync = require('load-json-file');

nanahira's avatar
nanahira committed
81 82
  util = require("util");

nanahira's avatar
nanahira committed
83 84
  Q = require("q");

nanahira's avatar
nanahira committed
85
  //heapdump = require 'heapdump'
nanahira's avatar
nanahira committed
86 87 88 89 90 91 92 93 94 95
  checkFileExists = async(path) => {
    var e;
    try {
      await fs.promises.access(path);
      return true;
    } catch (error1) {
      e = error1;
      return false;
    }
  };
nanahira's avatar
nanahira committed
96

nanahira's avatar
nanahira committed
97 98 99 100 101
  createDirectoryIfNotExists = async(path) => {
    if (!(await checkFileExists(path))) {
      return (await fs.promises.mkdir(path, {
        recursive: true
      }));
mercury233's avatar
mercury233 committed
102
    }
nanahira's avatar
nanahira committed
103
  };
mercury233's avatar
mercury233 committed
104

nanahira's avatar
fixes  
nanahira committed
105
  setting_save = global.setting_save = async function(settings) {
nanahira's avatar
nanahira committed
106
    var e;
nanahira's avatar
fixes  
nanahira committed
107 108 109 110 111
    try {
      await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2));
    } catch (error1) {
      e = error1;
      log.warn("setting save fail", e.toString());
nanahira's avatar
nanahira committed
112
    }
mercury233's avatar
mercury233 committed
113
  };
mercury233's avatar
mercury233 committed
114

nanahira's avatar
fixes  
nanahira committed
115
  setting_change = global.setting_change = async function(settings, path, val) {
mercury233's avatar
mercury233 committed
116
    var key, target;
mercury233's avatar
mercury233 committed
117
    if (_.isString(val)) {
nanahira's avatar
nanahira committed
118
      // path should be like "modules:welcome"
mercury233's avatar
mercury233 committed
119 120
      log.info("setting changed", path, val);
    }
mercury233's avatar
mercury233 committed
121 122 123 124 125 126 127 128 129 130 131 132
    path = path.split(':');
    if (path.length === 0) {
      settings[path[0]] = val;
    } else {
      target = settings;
      while (path.length > 1) {
        key = path.shift();
        target = target[key];
      }
      key = path.shift();
      target[key] = val;
    }
nanahira's avatar
fixes  
nanahira committed
133
    await setting_save(settings);
mercury233's avatar
mercury233 committed
134
  };
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
135

nanahira's avatar
nanahira committed
136 137
  importOldConfig = async function() {
    var e, oldbadwords, oldconfig, olddialogues, oldtips;
138
    try {
nanahira's avatar
nanahira committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
      oldconfig = (await loadJSONAsync('./config.user.json'));
      if (oldconfig.tips) {
        oldtips = {};
        oldtips.file = './config/tips.json';
        oldtips.tips = oldconfig.tips;
        await fs.promises.writeFile(oldtips.file, JSON.stringify(oldtips, null, 2));
        delete oldconfig.tips;
      }
      if (oldconfig.dialogues) {
        olddialogues = {};
        olddialogues.file = './config/dialogues.json';
        olddialogues.dialogues = oldconfig.dialogues;
        await fs.promises.writeFile(olddialogues.file, JSON.stringify(olddialogues, null, 2));
        delete oldconfig.dialogues;
      }
      oldbadwords = {};
      if (oldconfig.ban) {
        if (oldconfig.ban.badword_level0) {
          oldbadwords.level0 = oldconfig.ban.badword_level0;
        }
        if (oldconfig.ban.badword_level1) {
          oldbadwords.level1 = oldconfig.ban.badword_level1;
        }
        if (oldconfig.ban.badword_level2) {
          oldbadwords.level2 = oldconfig.ban.badword_level2;
        }
        if (oldconfig.ban.badword_level3) {
          oldbadwords.level3 = oldconfig.ban.badword_level3;
        }
      }
      if (!_.isEmpty(oldbadwords)) {
        oldbadwords.file = './config/badwords.json';
        await fs.promises.writeFile(oldbadwords.file, JSON.stringify(oldbadwords, null, 2));
        delete oldconfig.ban.badword_level0;
        delete oldconfig.ban.badword_level1;
        delete oldconfig.ban.badword_level2;
        delete oldconfig.ban.badword_level3;
      }
      if (!_.isEmpty(oldconfig)) {
        // log.info oldconfig
        await fs.promises.writeFile('./config/config.json', JSON.stringify(oldconfig, null, 2));
        log.info('imported old config from config.user.json');
      }
      return (await fs.promises.rename('./config.user.json', './config.user.bak'));
183 184
    } catch (error1) {
      e = error1;
nanahira's avatar
nanahira committed
185 186 187
      if (e.code !== 'ENOENT') {
        return log.info(e);
      }
mercury233's avatar
mercury233 committed
188
    }
nanahira's avatar
nanahira committed
189
  };
mercury233's avatar
mercury233 committed
190

nanahira's avatar
nanahira committed
191
  auth = global.auth = require('./ygopro-auth.js');
nanahira's avatar
nanahira committed
192

nanahira's avatar
nanahira committed
193
  ygopro = global.ygopro = require('./ygopro.js');
nanahira's avatar
nanahira committed
194

nanahira's avatar
nanahira committed
195
  roomlist = null;
mercury233's avatar
mercury233 committed
196

nanahira's avatar
nanahira committed
197
  settings = {};
mercury233's avatar
mercury233 committed
198

nanahira's avatar
nanahira committed
199
  tips = null;
mercury233's avatar
mercury233 committed
200

nanahira's avatar
nanahira committed
201
  dialogues = null;
mercury233's avatar
mercury233 committed
202

nanahira's avatar
nanahira committed
203
  badwords = null;
204

nanahira's avatar
nanahira committed
205
  lflists = global.lflists = [];
nanahira's avatar
nanahira committed
206

nanahira's avatar
nanahira committed
207
  real_windbot_server_ip = null;
nanahira's avatar
nanahira committed
208

nanahira's avatar
nanahira committed
209
  long_resolve_cards = [];
nanahira's avatar
nanahira committed
210

nanahira's avatar
nanahira committed
211
  ReplayParser = null;
nanahira's avatar
nanahira committed
212

nanahira's avatar
nanahira committed
213
  athleticChecker = null;
神楽坂玲奈's avatar
神楽坂玲奈 committed
214

nanahira's avatar
nanahira committed
215
  users_cache = {};
mercury233's avatar
mercury233 committed
216

nanahira's avatar
nanahira committed
217
  geoip = null;
nanahira's avatar
nanahira committed
218

nanahira's avatar
nanahira committed
219
  dataManager = null;
nanahira's avatar
nanahira committed
220

nanahira's avatar
nanahira committed
221
  disconnect_list = {}; // {old_client, old_server, room_id, timeout, deckbuf}
nanahira's avatar
nanahira committed
222

nanahira's avatar
nanahira committed
223
  challonge = null;
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
224

nanahira's avatar
nanahira committed
225 226 227 228
  challonge_cache = {
    participants: null,
    matches: null
  };
神楽坂玲奈's avatar
神楽坂玲奈 committed
229

nanahira's avatar
nanahira committed
230 231 232 233
  challonge_queue_callbacks = {
    participants: [],
    matches: []
  };
mercury233's avatar
mercury233 committed
234

nanahira's avatar
nanahira committed
235 236 237 238
  is_challonge_requesting = {
    participants: null,
    matches: null
  };
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
239

nanahira's avatar
nanahira committed
240
  get_callback = function() {};
nanahira's avatar
nanahira committed
241

nanahira's avatar
nanahira committed
242 243 244
  replaced_index = function() {};

  refresh_challonge_cache = function() {};
mercury233's avatar
merge  
mercury233 committed
245

nanahira's avatar
nanahira committed
246 247 248 249 250 251 252 253 254 255
  ResolveData = (function() {
    class ResolveData {
      constructor(func) {
        this.func = func;
      }

      resolve(err, data) {
        if (this.resolved) {
          return false;
        }
nanahira's avatar
fix  
nanahira committed
256
        this.resolved = true;
nanahira's avatar
nanahira committed
257 258 259 260 261 262 263 264 265 266 267 268
        this.func(err, data);
        return true;
      }

    };

    ResolveData.prototype.resolved = false;

    return ResolveData;

  }).call(this);

nanahira's avatar
nanahira committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
  loadLFList = function(path) {
    var date, j, len, list, ref, results;
    try {
      ref = fs.promises.readFile(path, 'utf8').match(/!.*/g);
      results = [];
      for (j = 0, len = ref.length; j < len; j++) {
        list = ref[j];
        date = list.match(/!([\d\.]+)/);
        if (!date) {
          continue;
        }
        results.push(lflists.push({
          date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"),
          tcg: list.indexOf('TCG') !== -1
        }));
      }
      return results;
    } catch (error1) {

    }
  };

  init = async function() {
    var AthleticChecker, DataManager, challonge_module_name, challonge_type, chat_color, config, cppversion, defaultConfig, default_data, dns, e, http_server, https, https_server, imported, j, l, len, len1, len2, m, mkdirList, options, pgClient, pg_client, pg_query, plugin_filename, plugin_list, plugin_path, postData, ref, results, windbots;
    await createDirectoryIfNotExists("./config");
    await importOldConfig();
    defaultConfig = (await loadJSONAsync('./data/default_config.json'));
    if ((await checkFileExists("./config/config.json"))) {
      try {
        config = (await loadJSONAsync('./config/config.json'));
      } catch (error1) {
        e = error1;
        console.error("Failed reading config: ", e.toString());
        process.exit(1);
      }
    } else {
      config = {};
    }
    settings = global.settings = merge(defaultConfig, config, {
      arrayMerge: function(destination, source) {
        return source;
      }
    });
    //import old configs
    imported = false;
    //reset http.quick_death_rule from true to 1
    if (settings.modules.http.quick_death_rule === true) {
      settings.modules.http.quick_death_rule = 1;
      imported = true;
    }
    //import the old passwords to new admin user system
    if (settings.modules.http.password) {
      await auth.add_user("olduser", settings.modules.http.password, true, {
        "get_rooms": true,
        "shout": true,
        "stop": true,
        "change_settings": true,
        "ban_user": true,
        "kick_user": true,
        "start_death": true
      });
      delete settings.modules.http.password;
      imported = true;
    }
    if (settings.modules.tournament_mode.password) {
      await auth.add_user("tournament", settings.modules.tournament_mode.password, true, {
        "duel_log": true,
        "download_replay": true,
        "clear_duel_log": true,
        "deck_dashboard_read": true,
        "deck_dashboard_write": true
      });
      delete settings.modules.tournament_mode.password;
      imported = true;
    }
    if (settings.modules.pre_util.password) {
      await auth.add_user("pre", settings.modules.pre_util.password, true, {
        "pre_dashboard": true
      });
      delete settings.modules.pre_util.password;
      imported = true;
    }
    if (settings.modules.update_util.password) {
      await auth.add_user("update", settings.modules.update_util.password, true, {
        "update_dashboard": true
      });
      delete settings.modules.update_util.password;
      imported = true;
    }
    //import the old enable_priority hostinfo
    if (settings.hostinfo.enable_priority || settings.hostinfo.enable_priority === false) {
      if (settings.hostinfo.enable_priority) {
        settings.hostinfo.duel_rule = 3;
      } else {
        settings.hostinfo.duel_rule = 5;
      }
      delete settings.hostinfo.enable_priority;
      imported = true;
    }
    //import the old Challonge api key option
    if (settings.modules.challonge.api_key) {
      settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key;
      delete settings.modules.challonge.api_key;
      imported = true;
    }
    //import the old random_duel.blank_pass_match option
    if (settings.modules.random_duel.blank_pass_match === true) {
      settings.modules.random_duel.blank_pass_modes = {
        "S": true,
        "M": true,
        "T": false
      };
      delete settings.modules.random_duel.blank_pass_match;
      imported = true;
    }
    if (settings.modules.random_duel.blank_pass_match === false) {
      settings.modules.random_duel.blank_pass_modes = {
        "S": true,
        "M": false,
        "T": false
      };
      delete settings.modules.random_duel.blank_pass_match;
      imported = true;
    }
    //finish
    if (imported) {
      await setting_save(settings);
    }
    // 读取数据
    default_data = (await loadJSONAsync('./data/default_data.json'));
    try {
      tips = global.tips = (await loadJSONAsync('./config/tips.json'));
    } catch (error1) {
      tips = global.tips = default_data.tips;
      await setting_save(tips);
404
    }
nanahira's avatar
nanahira committed
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    try {
      dialogues = global.dialogues = (await loadJSONAsync('./config/dialogues.json'));
    } catch (error1) {
      dialogues = global.dialogues = default_data.dialogues;
      await setting_save(dialogues);
    }
    try {
      badwords = global.badwords = (await loadJSONAsync('./config/badwords.json'));
    } catch (error1) {
      badwords = global.badwords = default_data.badwords;
      await setting_save(badwords);
    }
    if (settings.modules.chat_color.enabled) {
      try {
        chat_color = global.chat_color = (await loadJSONAsync('./config/chat_color.json'));
      } catch (error1) {
        chat_color = global.chat_color = default_data.chat_color;
        await setting_save(chat_color);
      }
    }
    try {
      cppversion = parseInt((await fs.promises.readFile('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1]), '16');
      await setting_change(settings, "version", cppversion);
      log.info("ygopro version 0x" + settings.version.toString(16), "(from source code)");
    } catch (error1) {
      //settings.version = settings.version_default
      log.info("ygopro version 0x" + settings.version.toString(16), "(from config)");
    }
    // load the lflist of current date
    await loadLFList('ygopro/expansions/lflist.conf');
    await loadLFList('ygopro/lflist.conf');
    if (settings.modules.windbot.enabled) {
      windbots = global.windbots = (await loadJSONAsync(settings.modules.windbot.botlist).windbots);
      real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip;
      if (!settings.modules.windbot.server_ip.includes("127.0.0.1")) {
        dns = require('dns');
        real_windbot_server_ip = global.real_windbot_server_ip = (await util.promisify(dns.lookup)(settings.modules.windbot.server_ip));
      }
    }
    if (settings.modules.heartbeat_detection.enabled) {
      long_resolve_cards = global.long_resolve_cards = (await loadJSONAsync('./data/long_resolve_cards.json'));
    }
    if (settings.modules.tournament_mode.enable_recover) {
      ReplayParser = global.ReplayParser = require("./Replay.js");
    }
    if (settings.modules.athletic_check.enabled) {
      AthleticChecker = require("./athletic-check.js").AthleticChecker;
      athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check);
    }
    if (settings.modules.http.websocket_roomlist) {
      roomlist = global.roomlist = require('./roomlist.js');
    }
    if (settings.modules.i18n.auto_pick) {
      geoip = require('geoip-country-lite');
    }
    if (settings.modules.mysql.enabled) {
      DataManager = require('./data-manager/DataManager.js').DataManager;
      dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log);
      await dataManager.init();
    } else {
      log.warn("Some functions may be limited without MySQL .");
      if (settings.modules.cloud_replay.enabled) {
        settings.modules.cloud_replay.enabled = false;
        await setting_save(settings);
        log.warn("Cloud replay cannot be enabled because no MySQL.");
      }
      if (settings.modules.enable_recover.enabled) {
        settings.modules.enable_recover.enabled = false;
        await setting_save(settings);
        log.warn("Recover mode cannot be enabled because no MySQL.");
      }
      if (settings.modules.chat_color.enabled) {
        settings.modules.chat_color.enabled = false;
        await setting_save(settings);
        log.warn("Chat color cannot be enabled because no MySQL.");
      }
    }
    if (settings.modules.mycard.enabled) {
      pgClient = require('pg').Client;
      pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database);
      pg_client.on('error', function(err) {
        log.warn("PostgreSQL ERROR: ", err);
      });
      pg_query = pg_client.query('SELECT username, id from users');
      pg_query.on('error', function(err) {
        log.warn("PostgreSQL Query ERROR: ", err);
      });
      pg_query.on('row', function(row) {
        //log.info "load user", row.username, row.id
        users_cache[row.username] = row.id;
      });
      pg_query.on('end', function(result) {
        log.info("users loaded", result.rowCount);
      });
      pg_client.on('drain', pg_client.end.bind(pg_client));
      log.info("loading mycard user...");
      pg_client.connect();
      if (settings.modules.arena_mode.enabled && settings.modules.arena_mode.init_post.enabled) {
        postData = qs.stringify({
          ak: settings.modules.arena_mode.init_post.accesskey,
          arena: settings.modules.arena_mode.mode
        });
        try {
          await axios.post(settings.modules.arena_mode.init_post.url + "?" + postData, {
            responseType: "json"
          });
        } catch (error1) {
          e = error1;
          log.warn('ARENA INIT POST ERROR', e);
        }
      }
    }
    if (settings.modules.challonge.enabled) {
      challonge_module_name = 'challonge';
      if (settings.modules.challonge.use_custom_module) {
        challonge_module_name = settings.modules.challonge.use_custom_module;
      }
      challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options);
nanahira's avatar
fix  
nanahira committed
523 524 525 526
      challonge_cache = {
        participants: null,
        matches: null
      };
nanahira's avatar
nanahira committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
      challonge_queue_callbacks = {
        participants: [],
        matches: []
      };
      is_challonge_requesting = {
        participants: null,
        matches: null
      };
      get_callback = function(challonge_type, resolve_data) {
        return (function(err, data) {
          var cur_resolve_data;
          if (settings.modules.challonge.cache_ttl && !err && data) {
            challonge_cache[challonge_type] = data;
          }
          is_challonge_requesting[challonge_type] = null;
          resolve_data.resolve(err, data);
          while (challonge_queue_callbacks[challonge_type].length) {
            cur_resolve_data = challonge_queue_callbacks[challonge_type].splice(0, 1)[0];
            cur_resolve_data.resolve(err, data);
          }
        });
      };
      replaced_index = function(challonge_type) {
        return function(_data) {
          var err, resolve_data;
          resolve_data = new ResolveData(_data.callback);
          if (settings.modules.challonge.cache_ttl && !_data.no_cache && challonge_cache[challonge_type]) {
            resolve_data.resolve(null, challonge_cache[challonge_type]);
          } else if (is_challonge_requesting[challonge_type] && moment() - is_challonge_requesting[challonge_type] <= 5000) {
            challonge_queue_callbacks[challonge_type].push(resolve_data);
          } else {
            _data.callback = get_callback(challonge_type, resolve_data);
            is_challonge_requesting[challonge_type] = moment();
            try {
              challonge[challonge_type].index(_data);
            } catch (error1) {
              err = error1;
              _data.callback(err, null);
            }
          }
        };
      };
      ref = ["participants", "matches"];
      for (j = 0, len = ref.length; j < len; j++) {
        challonge_type = ref[j];
        challonge[challonge_type]._index = replaced_index(challonge_type);
      }
      challonge.matches._update = function(_data) {
        var err;
        try {
          challonge.matches.update(_data);
        } catch (error1) {
          err = error1;
          log.warn("Errored pushing scores to Challonge.", err);
        }
      };
      refresh_challonge_cache = global.refresh_challonge_cache = function() {
        if (settings.modules.challonge.cache_ttl) {
          challonge_cache.participants = null;
          challonge_cache.matches = null;
        }
      };
      refresh_challonge_cache();
      if (settings.modules.challonge.cache_ttl) {
        setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl);
      }
    }
    if (settings.modules.tips.get) {
      load_tips();
      setInterval(function() {
        var l, len1, room;
        for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
          room = ROOM_all[l];
          if (room && room.established) {
            if (room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING || room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
              ygopro.stoc_send_random_tip_to_room(room);
            }
          }
        }
      }, 30000);
    }
    if (settings.modules.dialogues.get) {
      load_dialogues();
    }
    if (settings.modules.random_duel.post_match_scores) {
      setInterval(function() {
        var scores, scores_by_lose, scores_by_win, scores_pair;
        scores_pair = _.pairs(ROOM_players_scores);
        scores_by_lose = _.sortBy(scores_pair, function(score) {
          return score[1].lose;
        }).reverse(); // 败场由高到低
        scores_by_win = _.sortBy(scores_by_lose, function(score) {
          return score[1].win;
        }).reverse(); // 然后胜场由低到高,再逆转,就是先排胜场再排败场
        scores = _.first(scores_by_win, 10);
        //log.info scores
        request.post({
          url: settings.modules.random_duel.post_match_scores,
          form: {
            accesskey: settings.modules.random_duel.post_match_accesskey,
            rank: JSON.stringify(scores)
          }
        }, (error, response, body) => {
          if (error) {
            log.warn('RANDOM SCORE POST ERROR', error);
          } else {
            if (response.statusCode !== 204 && response.statusCode !== 200) {
              log.warn('RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body);
            }
          }
        });
      //else
      //  log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
      }, 60000);
nanahira's avatar
nanahira committed
641
    }
nanahira's avatar
nanahira committed
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
    if (settings.modules.random_duel.enabled) {
      setInterval(async function() {
        var l, len1, room, time_passed;
        for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
          room = ROOM_all[l];
          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)) {
            continue;
          }
          time_passed = Math.floor((moment() - room.last_active_time) / 1000);
          //log.info time_passed
          if (time_passed >= settings.modules.random_duel.hang_timeout) {
            room.last_active_time = moment();
            await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}");
            room.scores[room.waiting_for_player.name_vpass] = -9;
            //log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
            ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
            CLIENT_send_replays(room.waiting_for_player, room);
            CLIENT_kick(room.waiting_for_player);
          } else if (time_passed >= (settings.modules.random_duel.hang_timeout - 20) && !(time_passed % 10)) {
            ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${afk_warn_part1}${settings.modules.random_duel.hang_timeout - time_passed}\${afk_warn_part2}`, ygopro.constants.COLORS.RED);
            ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}");
          }
nanahira's avatar
nanahira committed
664
        }
nanahira's avatar
nanahira committed
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
      }, 1000);
    }
    if (settings.modules.mycard.enabled) {
      setInterval(function() {
        var l, len1, len2, m, player, room, time_passed, waited_time;
        for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
          room = ROOM_all[l];
          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)) {
            continue;
          }
          time_passed = Math.floor((moment() - room.last_active_time) / 1000);
          //log.info time_passed
          if (time_passed >= settings.modules.random_duel.hang_timeout) {
            room.last_active_time = moment();
            ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
            room.scores[room.waiting_for_player.name_vpass] = -9;
            //log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
            CLIENT_send_replays(room.waiting_for_player, room);
            CLIENT_kick(room.waiting_for_player);
          } else if (time_passed >= (settings.modules.random_duel.hang_timeout - 20) && !(time_passed % 10)) {
            ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${afk_warn_part1}${settings.modules.random_duel.hang_timeout - time_passed}\${afk_warn_part2}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
686
          }
nanahira's avatar
js  
nanahira committed
687
        }
nanahira's avatar
nanahira committed
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
        return;
        if (true) { // settings.modules.arena_mode.punish_quit_before_match
          for (m = 0, len2 = ROOM_all.length; m < len2; m++) {
            room = ROOM_all[m];
            if (!(room && room.arena && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.get_playing_player().length < 2)) {
              continue;
            }
            player = room.get_playing_player()[0];
            if (player && player.join_time && !player.arena_quit_free) {
              waited_time = moment() - player.join_time;
              if (waited_time >= 30000) {
                ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE);
                player.arena_quit_free = true;
              } else if (waited_time >= 5000 && waited_time < 6000) {
                ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE);
              }
            }
          }
        }
      }, 1000);
nanahira's avatar
nanahira committed
708
    }
nanahira's avatar
nanahira committed
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
    if (settings.modules.heartbeat_detection.enabled) {
      setInterval(function() {
        var l, len1, len2, m, player, ref1, room;
        for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
          room = ROOM_all[l];
          if (room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && (room.hostinfo.time_limit === 0 || room.duel_stage !== ygopro.constants.DUEL_STAGE.DUELING) && !room.windbot) {
            ref1 = room.get_playing_player();
            for (m = 0, len2 = ref1.length; m < len2; m++) {
              player = ref1[m];
              if (player && (room.duel_stage !== ygopro.constants.DUEL_STAGE.SIDING || player.selected_preduel)) {
                CLIENT_heartbeat_register(player, true);
              }
            }
          }
        }
      }, settings.modules.heartbeat_detection.interval);
    }
    if (settings.modules.windbot.enabled && settings.modules.windbot.spawn) {
      spawn_windbot();
    }
    setInterval(function() {
      var current_time, l, len1, results, room;
      current_time = moment();
      results = [];
      for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
        room = ROOM_all[l];
        if (!(room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && room.hostinfo.auto_death && !room.auto_death_triggered && current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death)) {
          continue;
        }
        room.auto_death_triggered = true;
        results.push(room.start_death());
nanahira's avatar
nanahira committed
740
      }
nanahira's avatar
nanahira committed
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
      return results;
    }, 1000);
    net.createServer(netRequestHandler).listen(settings.port, function() {
      log.info("server started", settings.port);
    });
    if (settings.modules.stop) {
      log.info("NOTE: server not open due to config, ", settings.modules.stop);
    }
    http_server = http.createServer(httpRequestListener);
    http_server.listen(settings.modules.http.port);
    if (settings.modules.http.ssl.enabled) {
      https = require('https');
      options = {
        cert: (await fs.promises.readFile(settings.modules.http.ssl.cert)),
        key: (await fs.promises.readFile(settings.modules.http.ssl.key))
      };
      https_server = https.createServer(options, httpRequestListener);
      if (settings.modules.http.websocket_roomlist && roomlist) {
        roomlist.init(https_server, ROOM_all);
nanahira's avatar
nanahira committed
760
      }
nanahira's avatar
nanahira committed
761
      https_server.listen(settings.modules.http.ssl.port);
nanahira's avatar
nanahira committed
762
    }
nanahira's avatar
nanahira committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
    mkdirList = ["./plugins", settings.modules.tournament_mode.deck_path, settings.modules.tournament_mode.replay_path, settings.modules.tournament_mode.log_save_path, settings.modules.deck_log.local];
    for (l = 0, len1 = mkdirList.length; l < len1; l++) {
      path = mkdirList[l];
      await createDirectoryIfNotExists(path);
    }
    plugin_list = (await fs.promises.readdir("./plugins"));
    results = [];
    for (m = 0, len2 = plugin_list.length; m < len2; m++) {
      plugin_filename = plugin_list[m];
      plugin_path = process.cwd() + "/plugins/" + plugin_filename;
      require(plugin_path);
      results.push(log.info("Plugin loaded:", plugin_filename));
    }
    return results;
  };
nanahira's avatar
nanahira committed
778

nanahira's avatar
nanahira committed
779
  // 获取可用内存
nanahira's avatar
nanahira committed
780
  memory_usage = global.memory_usage = 0;
mercury233's avatar
mercury233 committed
781

nanahira's avatar
nanahira committed
782 783 784 785
  get_memory_usage = global.get_memory_usage = function() {
    var percentUsed;
    percentUsed = os.freemem() * os.totalmem() * 100;
    memory_usage = global.memory_usage = percentUsed;
mercury233's avatar
mercury233 committed
786 787
  };

mercury233's avatar
mercury233 committed
788 789 790 791
  get_memory_usage();

  setInterval(get_memory_usage, 3000);

nanahira's avatar
nanahira committed
792
  ROOM_all = global.ROOM_all = [];
mercury233's avatar
mercury233 committed
793

nanahira's avatar
nanahira committed
794
  ROOM_players_oppentlist = global.ROOM_players_oppentlist = {};
mercury233's avatar
mercury233 committed
795

nanahira's avatar
nanahira committed
796
  ROOM_players_scores = global.ROOM_players_scores = {};
797

nanahira's avatar
nanahira committed
798
  ROOM_connected_ip = global.ROOM_connected_ip = {};
mercury233's avatar
mercury233 committed
799

nanahira's avatar
nanahira committed
800
  ROOM_bad_ip = global.ROOM_bad_ip = {};
mercury233's avatar
mercury233 committed
801

nanahira's avatar
nanahira committed
802
  // ban a user manually and permanently
nanahira's avatar
nanahira committed
803
  ban_user = global.ban_user = async function(name) {
nanahira's avatar
nanahira committed
804
    var ban, bans, j, l, len, len1, len2, len3, m, n, player, playerType, ref, ref1, room;
nanahira's avatar
nanahira committed
805 806 807
    if (!settings.modules.mysql.enabled) {
      throw "MySQL is not enabled";
    }
nanahira's avatar
nanahira committed
808
    bans = [dataManager.getBan(name, null)];
nanahira's avatar
nanahira committed
809 810
    for (j = 0, len = ROOM_all.length; j < len; j++) {
      room = ROOM_all[j];
nanahira's avatar
nanahira committed
811
      if (room && room.established) {
nanahira's avatar
nanahira committed
812 813 814 815 816 817
        ref = ["players", "watchers"];
        for (l = 0, len1 = ref.length; l < len1; l++) {
          playerType = ref[l];
          ref1 = room[playerType];
          for (m = 0, len2 = ref1.length; m < len2; m++) {
            player = ref1[m];
nanahira's avatar
nanahira committed
818 819 820 821 822 823 824
            if (!(player.name === name || bans.find(ban(() => {
              return player.ip === ban.ip;
            })))) {
              continue;
            }
            bans.push(dataManager.getBan(name, player.ip));
            ROOM_bad_ip[player.ip] = 99;
nanahira's avatar
nanahira committed
825
            ygopro.stoc_send_chat_to_room(room, `${player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
826 827 828
            CLIENT_send_replays(player, room);
            CLIENT_kick(player);
          }
nanahira's avatar
nanahira committed
829 830 831
        }
      }
    }
nanahira's avatar
nanahira committed
832 833
    for (n = 0, len3 = bans.length; n < len3; n++) {
      ban = bans[n];
nanahira's avatar
nanahira committed
834 835
      await dataManager.banPlayer(ban);
    }
mercury233's avatar
mercury233 committed
836 837
  };

nanahira's avatar
nanahira committed
838
  // automatically ban user to use random duel
nanahira's avatar
nanahira committed
839 840
  ROOM_ban_player = global.ROOM_ban_player = async function(name, ip, reason, countadd = 1) {
    if (settings.modules.test_mode.no_ban_player || !settings.modules.mysql.enabled) {
841 842
      return;
    }
nanahira's avatar
nanahira committed
843
    await dataManager.randomDuelBanPlayer(ip, reason, countadd);
mercury233's avatar
mercury233 committed
844 845
  };

nanahira's avatar
nanahira committed
846
  ROOM_kick = function(name, callback) {
nanahira's avatar
nanahira committed
847 848 849 850 851 852 853 854
    var found;
    found = false;
    return _async.each(ROOM_all, function(room, done) {
      if (!(room && room.established && (name === "all" || name === room.process_pid.toString() || name === room.name))) {
        done();
        return;
      }
      found = true;
nanahira's avatar
fixes  
nanahira committed
855
      room.terminate();
nanahira's avatar
nanahira committed
856
      return done();
nanahira's avatar
nanahira committed
857
    }, function(err) {
nanahira's avatar
nanahira committed
858
      callback(null, found);
nanahira's avatar
nanahira committed
859 860 861
    });
  };

nanahira's avatar
nanahira committed
862
  ROOM_player_win = global.ROOM_player_win = function(name) {
863 864 865 866 867 868 869 870 871 872 873 874
    if (!ROOM_players_scores[name]) {
      ROOM_players_scores[name] = {
        win: 0,
        lose: 0,
        flee: 0,
        combo: 0
      };
    }
    ROOM_players_scores[name].win = ROOM_players_scores[name].win + 1;
    ROOM_players_scores[name].combo = ROOM_players_scores[name].combo + 1;
  };

nanahira's avatar
nanahira committed
875
  ROOM_player_lose = global.ROOM_player_lose = function(name) {
876 877 878 879 880 881 882 883 884 885 886 887
    if (!ROOM_players_scores[name]) {
      ROOM_players_scores[name] = {
        win: 0,
        lose: 0,
        flee: 0,
        combo: 0
      };
    }
    ROOM_players_scores[name].lose = ROOM_players_scores[name].lose + 1;
    ROOM_players_scores[name].combo = 0;
  };

nanahira's avatar
nanahira committed
888
  ROOM_player_flee = global.ROOM_player_flee = function(name) {
889 890 891 892 893 894 895 896 897 898 899 900
    if (!ROOM_players_scores[name]) {
      ROOM_players_scores[name] = {
        win: 0,
        lose: 0,
        flee: 0,
        combo: 0
      };
    }
    ROOM_players_scores[name].flee = ROOM_players_scores[name].flee + 1;
    ROOM_players_scores[name].combo = 0;
  };

nanahira's avatar
nanahira committed
901
  ROOM_player_get_score = global.ROOM_player_get_score = function(player) {
902 903 904 905
    var name, score, total;
    name = player.name_vpass;
    score = ROOM_players_scores[name];
    if (!score) {
nanahira's avatar
nanahira committed
906
      return `${player.name} \${random_score_blank}`;
907
    }
mercury233's avatar
mercury233 committed
908
    total = score.win + score.lose;
909
    if (score.win < 2 && total < 3) {
nanahira's avatar
nanahira committed
910
      return `${player.name} \${random_score_not_enough}`;
911 912
    }
    if (score.combo >= 2) {
nanahira's avatar
nanahira committed
913
      return `\${random_score_part1}${player.name} \${random_score_part2} ${Math.ceil(score.win / total * 100)}\${random_score_part3} ${Math.ceil(score.flee / total * 100)}\${random_score_part4_combo}${score.combo}\${random_score_part5_combo}`;
914
    } else {
nanahira's avatar
nanahira committed
915 916
      //return player.name + " 的今日战绩:胜率" + Math.ceil(score.win/total*100) + "%,逃跑率" + Math.ceil(score.flee/total*100) + "%," + score.combo + "连胜中!"
      return `\${random_score_part1}${player.name} \${random_score_part2} ${Math.ceil(score.win / total * 100)}\${random_score_part3} ${Math.ceil(score.flee / total * 100)}\${random_score_part4}`;
917 918 919
    }
  };

nanahira's avatar
nanahira committed
920
  ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = async function(name, player_ip) {
nanahira's avatar
fixes  
nanahira committed
921
    var room, success, uname;
mercury233's avatar
mercury233 committed
922
    uname = name.toUpperCase();
mercury233's avatar
mercury233 committed
923
    if (settings.modules.windbot.enabled && (uname.slice(0, 2) === 'AI' || (!settings.modules.random_duel.enabled && uname === ''))) {
mercury233's avatar
mercury233 committed
924 925
      return ROOM_find_or_create_ai(name);
    }
mercury233's avatar
mercury233 committed
926
    if (settings.modules.random_duel.enabled && (uname === '' || uname === 'S' || uname === 'M' || uname === 'T')) {
nanahira's avatar
nanahira committed
927
      return (await ROOM_find_or_create_random(uname, player_ip));
mercury233's avatar
mercury233 committed
928 929 930
    }
    if (room = ROOM_find_by_name(name)) {
      return room;
mercury233's avatar
mercury233 committed
931
    } else if (memory_usage >= 90) {
mercury233's avatar
mercury233 committed
932 933
      return null;
    } else {
nanahira's avatar
fixes  
nanahira committed
934 935 936 937 938 939 940 941 942 943
      room = new Room(name);
      if (room.recover_duel_log_id) {
        success = (await room.initialize_recover());
        if (!success) {
          return {
            "error": "${cloud_replay_no}"
          };
        }
      }
      return room;
mercury233's avatar
mercury233 committed
944 945 946
    }
  };

nanahira's avatar
nanahira committed
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
  ROOM_find_or_create_random = global.ROOM_find_or_create_random = async function(type, player_ip) {
    var max_player, name, playerbanned, randomDuelBanRecord, result;
    if (settings.modules.mysql.enabled) {
      randomDuelBanRecord = (await dataManager.getRandomDuelBan(player_ip));
      if (randomDuelBanRecord) {
        if (randomDuelBanRecord.count > 6 && moment().isBefore(randomDuelBanRecord.time)) {
          return {
            "error": `\${random_banned_part1}${randomDuelBanRecord.reasons.join('${random_ban_reason_separator}')}\${random_banned_part2}${moment(randomDuelBanRecord.time).fromNow(true)}\${random_banned_part3}`
          };
        }
        if (randomDuelBanRecord.count > 3 && moment().isBefore(randomDuelBanRecord.time) && randomDuelBanRecord.getNeedTip() && type !== 'T') {
          randomDuelBanRecord.setNeedTip(false);
          await dataManager.updateRandomDuelBan(randomDuelBanRecord);
          return {
            "error": `\${random_deprecated_part1}${randomDuelBanRecord.reasons.join('${random_ban_reason_separator}')}\${random_deprecated_part2}${moment(randomDuelBanRecord.time).fromNow(true)}\${random_deprecated_part3}`
          };
        } else if (randomDuelBanRecord.getNeedTip()) {
          randomDuelBanRecord.setNeedTip(false);
          await dataManager.updateRandomDuelBan(randomDuelBanRecord);
          return {
            "error": `\${random_warn_part1}${randomDuelBanRecord.reasons.join('${random_ban_reason_separator}')}\${random_warn_part2}`
          };
        } else if (randomDuelBanRecord.count > 2) {
          randomDuelBanRecord.setNeedTip(true);
          await dataManager.updateRandomDuelBan(randomDuelBanRecord);
        }
mercury233's avatar
mercury233 committed
973 974 975
      }
    }
    max_player = type === 'T' ? 4 : 2;
nanahira's avatar
nanahira committed
976
    playerbanned = randomDuelBanRecord && randomDuelBanRecord.count > 3 && moment() < randomDuelBanRecord.time;
mercury233's avatar
mercury233 committed
977
    result = _.find(ROOM_all, function(room) {
978
      return room && room.random_type !== '' && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && !room.windbot && ((type === '' && (room.random_type === settings.modules.random_duel.default_type || settings.modules.random_duel.blank_pass_modes[room.random_type])) || room.random_type === type) && room.get_playing_player().length < max_player && (settings.modules.random_duel.no_rematch_check || room.get_host() === null || room.get_host().ip !== ROOM_players_oppentlist[player_ip]) && (playerbanned === room.deprecated || type === 'T');
mercury233's avatar
mercury233 committed
979 980
    });
    if (result) {
mercury233's avatar
mercury233 committed
981
      result.welcome = '${random_duel_enter_room_waiting}';
nanahira's avatar
nanahira committed
982
    //log.info 'found room', player_name
mercury233's avatar
mercury233 committed
983
    } else if (memory_usage < 90) {
984
      type = type ? type : settings.modules.random_duel.default_type;
mercury233's avatar
mercury233 committed
985 986 987 988
      name = type + ',RANDOM#' + Math.floor(Math.random() * 100000);
      result = new Room(name);
      result.random_type = type;
      result.max_player = max_player;
mercury233's avatar
mercury233 committed
989
      result.welcome = '${random_duel_enter_room_new}';
mercury233's avatar
mercury233 committed
990
      result.deprecated = playerbanned;
mercury233's avatar
mercury233 committed
991
    } else {
nanahira's avatar
nanahira committed
992
      //log.info 'create room', player_name, name
mercury233's avatar
mercury233 committed
993
      return null;
mercury233's avatar
mercury233 committed
994
    }
995 996 997
    if (result.random_type === 'S') {
      result.welcome2 = '${random_duel_enter_room_single}';
    }
mercury233's avatar
mercury233 committed
998
    if (result.random_type === 'M') {
999 1000 1001 1002
      result.welcome2 = '${random_duel_enter_room_match}';
    }
    if (result.random_type === 'T') {
      result.welcome2 = '${random_duel_enter_room_tag}';
mercury233's avatar
mercury233 committed
1003 1004 1005 1006
    }
    return result;
  };

nanahira's avatar
nanahira committed
1007
  ROOM_find_or_create_ai = global.ROOM_find_or_create_ai = function(name) {
1008
    var ainame, namea, result, room, uname, windbot;
mercury233's avatar
mercury233 committed
1009 1010 1011
    if (name === '') {
      name = 'AI';
    }
mercury233's avatar
mercury233 committed
1012
    namea = name.split('#');
1013
    uname = name.toUpperCase();
mercury233's avatar
mercury233 committed
1014 1015
    if (room = ROOM_find_by_name(name)) {
      return room;
1016
    } else if (uname === 'AI') {
1017 1018 1019
      windbot = _.sample(_.filter(windbots, function(w) {
        return !w.hidden;
      }));
mercury233's avatar
mercury233 committed
1020
      name = 'AI#' + Math.floor(Math.random() * 100000);
mercury233's avatar
mercury233 committed
1021 1022
    } else if (namea.length > 1) {
      ainame = namea[namea.length - 1];
mercury233's avatar
mercury233 committed
1023
      windbot = _.sample(_.filter(windbots, function(w) {
mercury233's avatar
mercury233 committed
1024 1025 1026 1027
        return w.name === ainame || w.deck === ainame;
      }));
      if (!windbot) {
        return {
mercury233's avatar
mercury233 committed
1028
          "error": "${windbot_deck_not_found}"
mercury233's avatar
mercury233 committed
1029 1030
        };
      }
mercury233's avatar
mercury233 committed
1031
      name = namea[0].toUpperCase() + '#N' + Math.floor(Math.random() * 100000);
mercury233's avatar
mercury233 committed
1032
    } else {
1033 1034 1035 1036
      windbot = _.sample(_.filter(windbots, function(w) {
        return !w.hidden;
      }));
      name = name + '#' + Math.floor(Math.random() * 10000);
mercury233's avatar
mercury233 committed
1037
    }
mercury233's avatar
mercury233 committed
1038 1039 1040
    if (name.replace(/[^\x00-\xff]/g, "00").length > 20) {
      log.info("long ai name", name);
      return {
mercury233's avatar
mercury233 committed
1041
        "error": "${windbot_name_too_long}"
mercury233's avatar
mercury233 committed
1042 1043
      };
    }
mercury233's avatar
mercury233 committed
1044 1045
    result = new Room(name);
    result.windbot = windbot;
nanahira's avatar
nanahira committed
1046
    result.private = true;
mercury233's avatar
mercury233 committed
1047 1048 1049
    return result;
  };

nanahira's avatar
nanahira committed
1050
  ROOM_find_by_name = global.ROOM_find_by_name = function(name) {
mercury233's avatar
mercury233 committed
1051 1052
    var result;
    result = _.find(ROOM_all, function(room) {
1053
      return room && room.name === name;
mercury233's avatar
mercury233 committed
1054 1055 1056 1057
    });
    return result;
  };

nanahira's avatar
nanahira committed
1058
  ROOM_find_by_title = global.ROOM_find_by_title = function(title) {
mercury233's avatar
mercury233 committed
1059 1060 1061 1062 1063 1064 1065
    var result;
    result = _.find(ROOM_all, function(room) {
      return room && room.title === title;
    });
    return result;
  };

nanahira's avatar
nanahira committed
1066
  ROOM_find_by_port = global.ROOM_find_by_port = function(port) {
mercury233's avatar
mercury233 committed
1067
    return _.find(ROOM_all, function(room) {
1068
      return room && room.port === port;
mercury233's avatar
mercury233 committed
1069 1070 1071
    });
  };

nanahira's avatar
nanahira committed
1072 1073 1074 1075 1076 1077
  ROOM_find_by_pid = global.ROOM_find_by_pid = function(pid) {
    return _.find(ROOM_all, function(room) {
      return room && room.process_pid === pid;
    });
  };

nanahira's avatar
nanahira committed
1078
  ROOM_validate = global.ROOM_validate = function(name) {
mercury233's avatar
mercury233 committed
1079 1080 1081 1082 1083 1084 1085 1086 1087
    var client_name, client_name_and_pass, client_pass;
    client_name_and_pass = name.split('$', 2);
    client_name = client_name_and_pass[0];
    client_pass = client_name_and_pass[1];
    if (!client_pass) {
      return true;
    }
    return !_.find(ROOM_all, function(room) {
      var room_name, room_name_and_pass, room_pass;
1088 1089 1090
      if (!room) {
        return false;
      }
mercury233's avatar
mercury233 committed
1091 1092 1093 1094 1095 1096 1097
      room_name_and_pass = room.name.split('$', 2);
      room_name = room_name_and_pass[0];
      room_pass = room_name_and_pass[1];
      return client_name === room_name && client_pass !== room_pass;
    });
  };

nanahira's avatar
nanahira committed
1098
  ROOM_unwelcome = global.ROOM_unwelcome = function(room, bad_player, reason) {
nanahira's avatar
nanahira committed
1099
    var j, len, player, ref;
mercury233's avatar
mercury233 committed
1100 1101 1102
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
1103 1104 1105
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
mercury233's avatar
mercury233 committed
1106
      if (player && player === bad_player) {
nanahira's avatar
nanahira committed
1107
        ygopro.stoc_send_chat(player, `\${unwelcome_warn_part1}${reason}\${unwelcome_warn_part2}`, ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
1108 1109
      } else if (player && player.pos !== 7 && player !== bad_player) {
        player.flee_free = true;
nanahira's avatar
nanahira committed
1110
        ygopro.stoc_send_chat(player, `\${unwelcome_tip_part1}${reason}\${unwelcome_tip_part2}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
1111 1112 1113 1114
      }
    }
  };

nanahira's avatar
nanahira committed
1115
  CLIENT_kick = global.CLIENT_kick = function(client) {
1116
    var room;
nanahira's avatar
nanahira committed
1117 1118 1119
    if (!client) {
      return false;
    }
nanahira's avatar
nanahira committed
1120
    client.system_kicked = true;
nanahira's avatar
typo  
nanahira committed
1121
    if (settings.modules.reconnect.enabled && client.closed) {
nanahira's avatar
fix  
nanahira committed
1122
      if (client.server && !client.had_new_reconnection) {
nanahira's avatar
fix  
nanahira committed
1123
        room = ROOM_all[client.rid];
1124 1125 1126
        if (room) {
          room.disconnect(client);
        } else {
nanahira's avatar
nanahira committed
1127
          SERVER_kick(client.server);
1128
        }
nanahira's avatar
fix  
nanahira committed
1129
      }
nanahira's avatar
nanahira committed
1130 1131 1132
    } else {
      client.destroy();
    }
nanahira's avatar
nanahira committed
1133
    return true;
nanahira's avatar
nanahira committed
1134 1135
  };

nanahira's avatar
nanahira committed
1136
  SERVER_kick = global.SERVER_kick = function(server) {
nanahira's avatar
typo  
nanahira committed
1137
    if (!server) {
nanahira's avatar
nanahira committed
1138 1139 1140 1141 1142 1143 1144
      return false;
    }
    server.system_kicked = true;
    server.destroy();
    return true;
  };

nanahira's avatar
nanahira committed
1145
  release_disconnect = global.release_disconnect = function(dinfo, reconnected) {
nanahira's avatar
nanahira committed
1146 1147 1148 1149
    if (dinfo.old_client && !reconnected) {
      dinfo.old_client.destroy();
    }
    if (dinfo.old_server && !reconnected) {
nanahira's avatar
nanahira committed
1150
      SERVER_kick(dinfo.old_server);
nanahira's avatar
nanahira committed
1151 1152 1153 1154
    }
    clearTimeout(dinfo.timeout);
  };

nanahira's avatar
nanahira committed
1155
  CLIENT_get_authorize_key = global.CLIENT_get_authorize_key = function(client) {
nanahira's avatar
js  
nanahira committed
1156
    if (!settings.modules.mycard.enabled && client.vpass) {
1157
      return client.name_vpass;
nanahira's avatar
js  
nanahira committed
1158 1159
    } else if (settings.modules.mycard.enabled || settings.modules.tournament_mode.enabled || settings.modules.challonge.enabled || client.is_local) {
      return client.name;
nanahira's avatar
nanahira committed
1160
    } else {
1161
      return client.ip + ":" + client.name;
nanahira's avatar
nanahira committed
1162 1163 1164
    }
  };

nanahira's avatar
nanahira committed
1165
  CLIENT_reconnect_unregister = global.CLIENT_reconnect_unregister = function(client, reconnected, exact) {
nanahira's avatar
nanahira committed
1166 1167 1168 1169
    if (!settings.modules.reconnect.enabled) {
      return false;
    }
    if (disconnect_list[CLIENT_get_authorize_key(client)]) {
nanahira's avatar
nanahira committed
1170 1171 1172
      if (exact && disconnect_list[CLIENT_get_authorize_key(client)].old_client !== client) {
        return false;
      }
nanahira's avatar
nanahira committed
1173 1174 1175 1176 1177 1178 1179
      release_disconnect(disconnect_list[CLIENT_get_authorize_key(client)], reconnected);
      delete disconnect_list[CLIENT_get_authorize_key(client)];
      return true;
    }
    return false;
  };

nanahira's avatar
nanahira committed
1180
  CLIENT_reconnect_register = global.CLIENT_reconnect_register = function(client, room_id, error) {
nanahira's avatar
nanahira committed
1181
    var dinfo, room, tmot;
nanahira's avatar
nanahira committed
1182
    room = ROOM_all[room_id];
nanahira's avatar
nanahira committed
1183 1184 1185
    if (client.had_new_reconnection) {
      return false;
    }
nanahira's avatar
nanahira committed
1186
    if (!settings.modules.reconnect.enabled || !room || client.system_kicked || client.flee_free || disconnect_list[CLIENT_get_authorize_key(client)] || client.is_post_watcher || !CLIENT_is_player(client, room) || room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || room.windbot || (settings.modules.reconnect.auto_surrender_after_disconnect && room.hostinfo.mode !== 1) || (room.random_type && room.get_disconnected_count() > 1)) {
nanahira's avatar
nanahira committed
1187 1188
      return false;
    }
nanahira's avatar
nanahira committed
1189 1190 1191
    // for player in room.players
    //   if player != client and CLIENT_get_authorize_key(player) == CLIENT_get_authorize_key(client)
    //     return false # some issues may occur in this case, so return false
nanahira's avatar
nanahira committed
1192
    dinfo = {
nanahira's avatar
nanahira committed
1193
      room_id: room_id,
nanahira's avatar
nanahira committed
1194 1195 1196 1197 1198 1199
      old_client: client,
      old_server: client.server,
      deckbuf: client.start_deckbuf
    };
    tmot = setTimeout(function() {
      room.disconnect(client, error);
nanahira's avatar
nanahira committed
1200
    //SERVER_kick(dinfo.old_server)
nanahira's avatar
nanahira committed
1201 1202 1203
    }, settings.modules.reconnect.wait_time);
    dinfo.timeout = tmot;
    disconnect_list[CLIENT_get_authorize_key(client)] = dinfo;
nanahira's avatar
nanahira committed
1204 1205
    //console.log("#{client.name} ${disconnect_from_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${disconnect_from_game}` + (error ? `: ${error}` : ''));
nanahira's avatar
nanahira committed
1206 1207 1208 1209
    if (client.time_confirm_required) {
      client.time_confirm_required = false;
      ygopro.ctos_send(client.server, 'TIME_CONFIRM');
    }
nanahira's avatar
nanahira committed
1210
    if (settings.modules.reconnect.auto_surrender_after_disconnect && room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
1211 1212
      ygopro.ctos_send(client.server, 'SURRENDER');
    }
nanahira's avatar
nanahira committed
1213 1214 1215
    return true;
  };

nanahira's avatar
nanahira committed
1216
  CLIENT_import_data = global.CLIENT_import_data = function(client, old_client, room) {
nanahira's avatar
nanahira committed
1217 1218 1219 1220
    var index, j, key, l, len, len1, player, ref;
    ref = room.players;
    for (index = j = 0, len = ref.length; j < len; index = ++j) {
      player = ref[index];
nanahira's avatar
nanahira committed
1221 1222 1223 1224 1225 1226
      if (player === old_client) {
        room.players[index] = client;
        break;
      }
    }
    room.dueling_players[old_client.pos] = client;
nanahira's avatar
nanahira committed
1227
    if (room.waiting_for_player === old_client) {
nanahira's avatar
nanahira committed
1228 1229
      room.waiting_for_player = client;
    }
nanahira's avatar
nanahira committed
1230
    if (room.waiting_for_player2 === old_client) {
nanahira's avatar
nanahira committed
1231 1232
      room.waiting_for_player2 = client;
    }
nanahira's avatar
nanahira committed
1233
    if (room.selecting_tp === old_client) {
nanahira's avatar
nanahira committed
1234 1235
      room.selecting_tp = client;
    }
nanahira's avatar
nanahira committed
1236 1237 1238
    if (room.determine_firstgo === old_client) {
      room.determine_firstgo = client;
    }
nanahira's avatar
nanahira committed
1239 1240
    for (l = 0, len1 = import_datas.length; l < len1; l++) {
      key = import_datas[l];
nanahira's avatar
nanahira committed
1241 1242
      client[key] = old_client[key];
    }
nanahira's avatar
nanahira committed
1243 1244 1245
    old_client.had_new_reconnection = true;
  };

nanahira's avatar
nanahira committed
1246
  SERVER_clear_disconnect = global.SERVER_clear_disconnect = function(server) {
nanahira's avatar
nanahira committed
1247 1248
    var k, v;
    if (!settings.modules.reconnect.enabled) {
nanahira's avatar
nanahira committed
1249
      return false;
nanahira's avatar
nanahira committed
1250 1251 1252 1253 1254 1255
    }
    for (k in disconnect_list) {
      v = disconnect_list[k];
      if (v && server === v.old_server) {
        release_disconnect(v);
        delete disconnect_list[k];
nanahira's avatar
nanahira committed
1256
        return true;
nanahira's avatar
nanahira committed
1257 1258
      }
    }
nanahira's avatar
nanahira committed
1259 1260 1261
    return false;
  };

nanahira's avatar
nanahira committed
1262
  ROOM_clear_disconnect = global.ROOM_clear_disconnect = function(room_id) {
nanahira's avatar
nanahira committed
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
    var k, v;
    if (!settings.modules.reconnect.enabled) {
      return false;
    }
    for (k in disconnect_list) {
      v = disconnect_list[k];
      if (v && room_id === v.room_id) {
        release_disconnect(v);
        delete disconnect_list[k];
        return true;
      }
    }
    return false;
nanahira's avatar
nanahira committed
1276 1277
  };

nanahira's avatar
nanahira committed
1278
  CLIENT_is_player = global.CLIENT_is_player = function(client, room) {
nanahira's avatar
nanahira committed
1279
    var is_player, j, len, player, ref;
nanahira's avatar
nanahira committed
1280
    is_player = false;
nanahira's avatar
nanahira committed
1281 1282 1283
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
nanahira's avatar
nanahira committed
1284 1285 1286 1287 1288
      if (client === player) {
        is_player = true;
        break;
      }
    }
nanahira's avatar
fix  
nanahira committed
1289
    return is_player && client.pos <= 3;
nanahira's avatar
nanahira committed
1290 1291
  };

nanahira's avatar
nanahira committed
1292
  CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1293
    var disconnect_info, room;
nanahira's avatar
nanahira committed
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
    if (!settings.modules.reconnect.enabled) {
      return false;
    }
    if (client.system_kicked) {
      return false;
    }
    disconnect_info = disconnect_list[CLIENT_get_authorize_key(client)];
    if (!disconnect_info) {
      return false;
    }
nanahira's avatar
nanahira committed
1304 1305 1306 1307 1308
    room = ROOM_all[disconnect_info.room_id];
    if (!room) {
      CLIENT_reconnect_unregister(client);
      return false;
    }
nanahira's avatar
nanahira committed
1309 1310 1311 1312 1313 1314
    if (deckbuf && !_.isEqual(deckbuf, disconnect_info.deckbuf)) {
      return false;
    }
    return true;
  };

nanahira's avatar
nanahira committed
1315
  CLIENT_get_kick_reconnect_target = global.CLIENT_get_kick_reconnect_target = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1316 1317 1318
    var j, l, len, len1, player, ref, room;
    for (j = 0, len = ROOM_all.length; j < len; j++) {
      room = ROOM_all[j];
nanahira's avatar
nanahira committed
1319
      if (room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && !room.windbot) {
nanahira's avatar
nanahira committed
1320 1321 1322
        ref = room.get_playing_player();
        for (l = 0, len1 = ref.length; l < len1; l++) {
          player = ref[l];
1323
          if (!player.closed && 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 || _.isEqual(player.start_deckbuf, deckbuf))) {
nanahira's avatar
nanahira committed
1324 1325 1326 1327 1328 1329 1330 1331
            return player;
          }
        }
      }
    }
    return null;
  };

nanahira's avatar
nanahira committed
1332
  CLIENT_is_able_to_kick_reconnect = global.CLIENT_is_able_to_kick_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1333 1334 1335 1336 1337 1338
    if (!(settings.modules.reconnect.enabled && settings.modules.reconnect.allow_kick_reconnect)) {
      return false;
    }
    if (!CLIENT_get_kick_reconnect_target(client, deckbuf)) {
      return false;
    }
nanahira's avatar
nanahira committed
1339 1340 1341
    return true;
  };

nanahira's avatar
nanahira committed
1342
  CLIENT_send_pre_reconnect_info = global.CLIENT_send_pre_reconnect_info = function(client, room, old_client) {
nanahira's avatar
nanahira committed
1343
    var j, len, player, ref, req_pos;
nanahira's avatar
nanahira committed
1344
    ygopro.stoc_send_chat(client, "${pre_reconnecting_to_room}", ygopro.constants.COLORS.BABYBLUE);
1345
    ygopro.stoc_send(client, 'JOIN_GAME', room.join_game_buffer);
nanahira's avatar
nanahira committed
1346 1347 1348 1349 1350 1351 1352
    req_pos = old_client.pos;
    if (old_client.is_host) {
      req_pos += 0x10;
    }
    ygopro.stoc_send(client, 'TYPE_CHANGE', {
      type: req_pos
    });
nanahira's avatar
nanahira committed
1353 1354 1355
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
nanahira's avatar
nanahira committed
1356
      ygopro.stoc_send(client, 'HS_PLAYER_ENTER', {
nanahira's avatar
nanahira committed
1357 1358
        name: player.name,
        pos: player.pos
nanahira's avatar
nanahira committed
1359
      });
nanahira's avatar
nanahira committed
1360 1361 1362
    }
  };

nanahira's avatar
nanahira committed
1363
  CLIENT_send_reconnect_info = global.CLIENT_send_reconnect_info = function(client, server, room) {
nanahira's avatar
nanahira committed
1364 1365
    client.reconnecting = true;
    ygopro.stoc_send_chat(client, "${reconnecting_to_room}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
    switch (room.duel_stage) {
      case ygopro.constants.DUEL_STAGE.FINGER:
        ygopro.stoc_send(client, 'DUEL_START');
        if ((room.hostinfo.mode !== 2 || client.pos === 0 || client.pos === 2) && !client.selected_preduel) {
          ygopro.stoc_send(client, 'SELECT_HAND');
        }
        client.reconnecting = false;
        break;
      case ygopro.constants.DUEL_STAGE.FIRSTGO:
        ygopro.stoc_send(client, 'DUEL_START');
nanahira's avatar
nanahira committed
1376
        if (client === room.selecting_tp) { // and !client.selected_preduel
nanahira's avatar
nanahira committed
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
          ygopro.stoc_send(client, 'SELECT_TP');
        }
        client.reconnecting = false;
        break;
      case ygopro.constants.DUEL_STAGE.SIDING:
        ygopro.stoc_send(client, 'DUEL_START');
        if (!client.selected_preduel) {
          ygopro.stoc_send(client, 'CHANGE_SIDE');
        }
        client.reconnecting = false;
        break;
      default:
        ygopro.ctos_send(server, 'REQUEST_FIELD');
        break;
nanahira's avatar
nanahira committed
1391 1392 1393
    }
  };

nanahira's avatar
nanahira committed
1394
  CLIENT_pre_reconnect = global.CLIENT_pre_reconnect = function(client) {
nanahira's avatar
nanahira committed
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407
    var dinfo, player;
    if (CLIENT_is_able_to_reconnect(client)) {
      dinfo = disconnect_list[CLIENT_get_authorize_key(client)];
      client.pre_reconnecting = true;
      client.pos = dinfo.old_client.pos;
      client.setTimeout(300000);
      CLIENT_send_pre_reconnect_info(client, ROOM_all[dinfo.room_id], dinfo.old_client);
    } else if (CLIENT_is_able_to_kick_reconnect(client)) {
      player = CLIENT_get_kick_reconnect_target(client);
      client.pre_reconnecting = true;
      client.pos = player.pos;
      client.setTimeout(300000);
      CLIENT_send_pre_reconnect_info(client, ROOM_all[player.rid], player);
nanahira's avatar
nanahira committed
1408 1409 1410
    }
  };

nanahira's avatar
nanahira committed
1411
  CLIENT_reconnect = global.CLIENT_reconnect = function(client) {
nanahira's avatar
nanahira committed
1412
    var current_old_server, dinfo, room;
nanahira's avatar
nanahira committed
1413 1414 1415 1416 1417 1418 1419
    if (!CLIENT_is_able_to_reconnect(client)) {
      ygopro.stoc_send_chat(client, "${reconnect_failed}", ygopro.constants.COLORS.RED);
      CLIENT_kick(client);
      return;
    }
    client.pre_reconnecting = false;
    dinfo = disconnect_list[CLIENT_get_authorize_key(client)];
nanahira's avatar
nanahira committed
1420
    room = ROOM_all[dinfo.room_id];
nanahira's avatar
nanahira committed
1421 1422 1423 1424 1425 1426
    current_old_server = client.server;
    client.server = dinfo.old_server;
    client.server.client = client;
    dinfo.old_client.server = null;
    current_old_server.client = null;
    current_old_server.had_new_reconnection = true;
nanahira's avatar
nanahira committed
1427
    SERVER_kick(current_old_server);
nanahira's avatar
nanahira committed
1428 1429
    client.established = true;
    client.pre_establish_buffers = [];
nanahira's avatar
nanahira committed
1430 1431 1432
    if (room.random_type || room.arena) {
      room.last_active_time = moment();
    }
nanahira's avatar
nanahira committed
1433 1434
    CLIENT_import_data(client, dinfo.old_client, room);
    CLIENT_send_reconnect_info(client, client.server, room);
nanahira's avatar
nanahira committed
1435 1436
    //console.log("#{client.name} ${reconnect_to_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${reconnect_to_game}`);
nanahira's avatar
nanahira committed
1437 1438 1439
    CLIENT_reconnect_unregister(client, true);
  };

nanahira's avatar
nanahira committed
1440
  CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
    var current_old_server, player, room;
    if (!CLIENT_is_able_to_kick_reconnect(client)) {
      ygopro.stoc_send_chat(client, "${reconnect_failed}", ygopro.constants.COLORS.RED);
      CLIENT_kick(client);
      return;
    }
    client.pre_reconnecting = false;
    player = CLIENT_get_kick_reconnect_target(client, deckbuf);
    room = ROOM_all[player.rid];
    current_old_server = client.server;
    client.server = player.server;
    client.server.client = client;
    ygopro.stoc_send_chat(player, "${reconnect_kicked}", ygopro.constants.COLORS.RED);
    player.server = null;
    player.had_new_reconnection = true;
    CLIENT_kick(player);
    current_old_server.client = null;
    current_old_server.had_new_reconnection = true;
nanahira's avatar
nanahira committed
1459
    SERVER_kick(current_old_server);
nanahira's avatar
nanahira committed
1460 1461 1462 1463 1464 1465 1466
    client.established = true;
    client.pre_establish_buffers = [];
    if (room.random_type || room.arena) {
      room.last_active_time = moment();
    }
    CLIENT_import_data(client, player, room);
    CLIENT_send_reconnect_info(client, client.server, room);
nanahira's avatar
nanahira committed
1467 1468
    //console.log("#{client.name} ${reconnect_to_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${reconnect_to_game}`);
nanahira's avatar
nanahira committed
1469 1470 1471
    CLIENT_reconnect_unregister(client, true);
  };

nanahira's avatar
nanahira committed
1472
  CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = function(client) {
nanahira's avatar
nanahira committed
1473 1474 1475 1476 1477
    if (!settings.modules.heartbeat_detection.enabled || !client.heartbeat_timeout) {
      return false;
    }
    clearTimeout(client.heartbeat_timeout);
    delete client.heartbeat_timeout;
nanahira's avatar
nanahira committed
1478
    //log.info(2, client.name)
nanahira's avatar
nanahira committed
1479 1480 1481
    return true;
  };

nanahira's avatar
nanahira committed
1482
  CLIENT_heartbeat_register = global.CLIENT_heartbeat_register = function(client, send) {
1483
    if (!settings.modules.heartbeat_detection.enabled || client.closed || client.is_post_watcher || client.pre_reconnecting || client.reconnecting || client.waiting_for_last || client.pos > 3 || client.heartbeat_protected) {
nanahira's avatar
nanahira committed
1484 1485 1486 1487 1488 1489 1490 1491
      return false;
    }
    if (client.heartbeat_timeout) {
      CLIENT_heartbeat_unregister(client);
    }
    client.heartbeat_responsed = false;
    if (send) {
      ygopro.stoc_send(client, "TIME_LIMIT", {
nanahira's avatar
nanahira committed
1492 1493 1494 1495 1496
        player: 0,
        left_time: 0
      });
      ygopro.stoc_send(client, "TIME_LIMIT", {
        player: 1,
nanahira's avatar
nanahira committed
1497 1498 1499 1500 1501 1502 1503 1504
        left_time: 0
      });
    }
    client.heartbeat_timeout = setTimeout(function() {
      CLIENT_heartbeat_unregister(client);
      if (!(client.closed || client.heartbeat_responsed)) {
        client.destroy();
      }
1505
    }, settings.modules.heartbeat_detection.wait_time);
nanahira's avatar
nanahira committed
1506
    //log.info(1, client.name)
nanahira's avatar
nanahira committed
1507 1508 1509
    return true;
  };

nanahira's avatar
nanahira committed
1510
  CLIENT_is_banned_by_mc = global.CLIENT_is_banned_by_mc = function(client) {
nanahira's avatar
nanahira committed
1511 1512 1513
    return client.ban_mc && client.ban_mc.banned && moment().isBefore(client.ban_mc.until);
  };

nanahira's avatar
nanahira committed
1514
  CLIENT_send_replays = global.CLIENT_send_replays = function(client, room) {
nanahira's avatar
nanahira committed
1515
    var buffer, i, j, len, ref;
1516
    if (!(settings.modules.replay_delay && !(settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player) && room.replays.length && room.hostinfo.mode === 1 && !client.replays_sent && !client.closed)) {
nanahira's avatar
nanahira committed
1517 1518
      return false;
    }
nanahira's avatar
nanahira committed
1519
    client.replays_sent = true;
nanahira's avatar
nanahira committed
1520
    i = 0;
nanahira's avatar
nanahira committed
1521 1522 1523
    ref = room.replays;
    for (j = 0, len = ref.length; j < len; j++) {
      buffer = ref[j];
nanahira's avatar
nanahira committed
1524
      ++i;
nanahira's avatar
fix  
nanahira committed
1525 1526 1527 1528
      if (buffer) {
        ygopro.stoc_send_chat(client, "${replay_hint_part1}" + i + "${replay_hint_part2}", ygopro.constants.COLORS.BABYBLUE);
        ygopro.stoc_send(client, "REPLAY", buffer);
      }
nanahira's avatar
nanahira committed
1529 1530 1531 1532
    }
    return true;
  };

nanahira's avatar
nanahira committed
1533
  SOCKET_flush_data = global.SOCKET_flush_data = function(sk, datas) {
nanahira's avatar
nanahira committed
1534
    var buffer, j, len;
nanahira's avatar
js  
nanahira committed
1535 1536 1537
    if (!sk || sk.closed) {
      return false;
    }
nanahira's avatar
nanahira committed
1538 1539
    for (j = 0, len = datas.length; j < len; j++) {
      buffer = datas[j];
1540 1541 1542
      sk.write(buffer);
    }
    datas.splice(0, datas.length);
nanahira's avatar
js  
nanahira committed
1543
    return true;
1544 1545
  };

nanahira's avatar
nanahira committed
1546
  getSeedTimet = global.getSeedTimet = function(count) {
nanahira's avatar
nanahira committed
1547
    var curTime, i, j, offset, ref, ret;
nanahira's avatar
nanahira committed
1548
    ret = [];
nanahira's avatar
nanahira committed
1549
    for (i = j = 0, ref = count; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
nanahira's avatar
nanahira committed
1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
      curTime = null;
      while (!curTime || _.any(ret, function(time) {
          return time === curTime.unix();
        })) {
        curTime = moment();
        offset = Math.floor(Math.random() * 240) - 120;
        if (offset > 0) {
          curTime = curTime.add(offset, "s");
        } else if (offset < 0) {
          curTime = curTime.subtract(-offset, "s");
        }
      }
      ret.push(curTime.unix());
    }
    ret.sort(function(a, b) {
      return a - b;
    });
    return ret;
  };

nanahira's avatar
nanahira committed
1570 1571
  Room = class Room {
    constructor(name, hostinfo) {
nanahira's avatar
fixes  
nanahira committed
1572
      var death_time, draw_count, duel_rule, lflist, param, rule, start_hand, start_lp, time_limit;
mercury233's avatar
mercury233 committed
1573 1574
      this.hostinfo = hostinfo;
      this.name = name;
nanahira's avatar
nanahira committed
1575
      //@alive = true
mercury233's avatar
mercury233 committed
1576 1577 1578
      this.players = [];
      this.player_datas = [];
      this.status = 'starting';
nanahira's avatar
nanahira committed
1579
      //@started = false
mercury233's avatar
mercury233 committed
1580 1581 1582
      this.established = false;
      this.watcher_buffers = [];
      this.recorder_buffers = [];
1583
      this.cloud_replay_id = Math.floor(Math.random() * 100000000);
mercury233's avatar
mercury233 committed
1584 1585 1586
      this.watchers = [];
      this.random_type = '';
      this.welcome = '';
mercury233's avatar
mercury233 committed
1587
      this.scores = {};
nanahira's avatar
nanahira committed
1588
      this.decks = {};
1589
      this.duel_count = 0;
nanahira's avatar
nanahira committed
1590
      this.death = 0;
nanahira's avatar
nanahira committed
1591
      this.turn = 0;
nanahira's avatar
nanahira committed
1592
      this.duel_stage = ygopro.constants.DUEL_STAGE.BEGIN;
nanahira's avatar
nanahira committed
1593
      this.replays = [];
nanahira's avatar
nanahira committed
1594
      this.first_list = [];
mercury233's avatar
mercury233 committed
1595
      ROOM_all.push(this);
mercury233's avatar
fix  
mercury233 committed
1596
      this.hostinfo || (this.hostinfo = JSON.parse(JSON.stringify(settings.hostinfo)));
nanahira's avatar
nanahira committed
1597
      delete this.hostinfo.comment;
mercury233's avatar
mercury233 committed
1598
      if (lflists.length) {
1599
        if (this.hostinfo.rule === 1 && this.hostinfo.lflist === 0) {
mercury233's avatar
mercury233 committed
1600
          this.hostinfo.lflist = _.findIndex(lflists, function(list) {
1601 1602 1603 1604 1605 1606
            return list.tcg;
          });
        }
      } else {
        this.hostinfo.lflist = -1;
      }
mercury233's avatar
mercury233 committed
1607 1608 1609 1610 1611
      if (name.slice(0, 2) === 'M#') {
        this.hostinfo.mode = 1;
      } else if (name.slice(0, 2) === 'T#') {
        this.hostinfo.mode = 2;
        this.hostinfo.start_lp = 16000;
mercury233's avatar
mercury233 committed
1612 1613 1614
      } else if (name.slice(0, 3) === 'AI#') {
        this.hostinfo.rule = 2;
        this.hostinfo.lflist = -1;
mercury233's avatar
mercury233 committed
1615
        this.hostinfo.time_limit = 999;
mercury233's avatar
mercury233 committed
1616 1617 1618
      } else if ((param = name.match(/^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i))) {
        this.hostinfo.rule = parseInt(param[1]);
        this.hostinfo.mode = parseInt(param[2]);
1619
        this.hostinfo.duel_rule = (param[3] === 'T' ? 3 : 4);
mercury233's avatar
mercury233 committed
1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635
        this.hostinfo.no_check_deck = param[4] === 'T';
        this.hostinfo.no_shuffle_deck = param[5] === 'T';
        this.hostinfo.start_lp = parseInt(param[6]);
        this.hostinfo.start_hand = parseInt(param[7]);
        this.hostinfo.draw_count = parseInt(param[8]);
      } else if ((param = name.match(/(.+)#/)) !== null) {
        rule = param[1].toUpperCase();
        if (rule.match(/(^|,|,)(M|MATCH)(,|,|$)/)) {
          this.hostinfo.mode = 1;
        }
        if (rule.match(/(^|,|,)(T|TAG)(,|,|$)/)) {
          this.hostinfo.mode = 2;
          this.hostinfo.start_lp = 16000;
        }
        if (rule.match(/(^|,|,)(TCGONLY|TO)(,|,|$)/)) {
          this.hostinfo.rule = 1;
mercury233's avatar
mercury233 committed
1636
          this.hostinfo.lflist = _.findIndex(lflists, function(list) {
mercury233's avatar
mercury233 committed
1637
            return list.tcg;
mercury233's avatar
mercury233 committed
1638 1639 1640 1641
          });
        }
        if (rule.match(/(^|,|,)(OCGONLY|OO)(,|,|$)/)) {
          this.hostinfo.rule = 0;
1642
          this.hostinfo.lflist = 0;
mercury233's avatar
mercury233 committed
1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
        }
        if (rule.match(/(^|,|,)(OT|TCG)(,|,|$)/)) {
          this.hostinfo.rule = 2;
        }
        if ((param = rule.match(/(^|,|,)LP(\d+)(,|,|$)/))) {
          start_lp = parseInt(param[2]);
          if (start_lp <= 0) {
            start_lp = 1;
          }
          if (start_lp >= 99999) {
            start_lp = 99999;
          }
          this.hostinfo.start_lp = start_lp;
        }
        if ((param = rule.match(/(^|,|,)(TIME|TM|TI)(\d+)(,|,|$)/))) {
          time_limit = parseInt(param[3]);
          if (time_limit < 0) {
            time_limit = 180;
          }
          if (time_limit >= 1 && time_limit <= 60) {
            time_limit = time_limit * 60;
          }
          if (time_limit >= 999) {
            time_limit = 999;
          }
          this.hostinfo.time_limit = time_limit;
        }
        if ((param = rule.match(/(^|,|,)(START|ST)(\d+)(,|,|$)/))) {
          start_hand = parseInt(param[3]);
          if (start_hand <= 0) {
            start_hand = 1;
          }
          if (start_hand >= 40) {
            start_hand = 40;
          }
          this.hostinfo.start_hand = start_hand;
        }
        if ((param = rule.match(/(^|,|,)(DRAW|DR)(\d+)(,|,|$)/))) {
          draw_count = parseInt(param[3]);
          if (draw_count >= 35) {
            draw_count = 35;
          }
          this.hostinfo.draw_count = draw_count;
        }
        if ((param = rule.match(/(^|,|,)(LFLIST|LF)(\d+)(,|,|$)/))) {
          lflist = parseInt(param[3]) - 1;
          this.hostinfo.lflist = lflist;
        }
        if (rule.match(/(^|,|,)(NOLFLIST|NF)(,|,|$)/)) {
          this.hostinfo.lflist = -1;
        }
        if (rule.match(/(^|,|,)(NOUNIQUE|NU)(,|,|$)/)) {
          this.hostinfo.rule = 3;
        }
        if (rule.match(/(^|,|,)(NOCHECK|NC)(,|,|$)/)) {
          this.hostinfo.no_check_deck = true;
        }
        if (rule.match(/(^|,|,)(NOSHUFFLE|NS)(,|,|$)/)) {
          this.hostinfo.no_shuffle_deck = true;
        }
nanahira's avatar
nanahira committed
1703
        if (rule.match(/(^|,|,)(IGPRIORITY|PR)(,|,|$)/)) { // deprecated
nanahira's avatar
nanahira committed
1704
          this.hostinfo.duel_rule = 4;
1705 1706 1707
        }
        if ((param = rule.match(/(^|,|,)(DUELRULE|MR)(\d+)(,|,|$)/))) {
          duel_rule = parseInt(param[3]);
mercury233's avatar
mercury233 committed
1708
          if (duel_rule && duel_rule > 0 && duel_rule <= 5) {
1709 1710
            this.hostinfo.duel_rule = duel_rule;
          }
mercury233's avatar
mercury233 committed
1711
        }
nanahira's avatar
nanahira committed
1712
        if (rule.match(/(^|,|,)(NOWATCH|NW)(,|,|$)/)) {
nanahira's avatar
nanahira committed
1713
          this.hostinfo.no_watch = true;
nanahira's avatar
nanahira committed
1714
        }
nanahira's avatar
nanahira committed
1715 1716 1717 1718 1719 1720 1721 1722
        if ((param = rule.match(/(^|,|,)(DEATH|DH)(\d*)(,|,|$)/))) {
          death_time = parseInt(param[3]);
          if (death_time && death_time > 0) {
            this.hostinfo.auto_death = death_time;
          } else {
            this.hostinfo.auto_death = 40;
          }
        }
nanahira's avatar
nanahira committed
1723
        if (settings.modules.tournament_mode.enable_recover && (param = rule.match(/(^|,|,)(RC|RECOVER)(\d*)T(\d*)(,|,|$)/))) {
nanahira's avatar
nanahira committed
1724 1725 1726
          this.recovered = true;
          this.recovering = true;
          this.recover_from_turn = parseInt(param[4]);
nanahira's avatar
fixes  
nanahira committed
1727
          this.recover_duel_log_id = parseInt(param[3]);
nanahira's avatar
nanahira committed
1728 1729 1730
          this.recover_buffers = [[], [], [], []];
          this.welcome = "${recover_hint}";
        }
mercury233's avatar
mercury233 committed
1731
      }
nanahira's avatar
nanahira committed
1732
      this.hostinfo.replay_mode = 0; // 0x1: Save the replays in file. 0x2: Block the replays to observers.
1733 1734 1735
      if (settings.modules.tournament_mode.enabled) {
        this.hostinfo.replay_mode |= 0x1;
      }
1736
      if ((settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player) || (this.hostinfo.mode === 1 && settings.modules.replay_delay)) {
1737 1738
        this.hostinfo.replay_mode |= 0x2;
      }
nanahira's avatar
fixes  
nanahira committed
1739 1740 1741 1742 1743 1744
      if (!this.recovered) {
        this.spawn();
      }
    }

    spawn(firstSeed) {
nanahira's avatar
nanahira committed
1745
      var i, j, l, param, seeds;
1746
      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];
nanahira's avatar
fixes  
nanahira committed
1747 1748
      if (firstSeed) {
        param.push(firstSeed);
nanahira's avatar
nanahira committed
1749
        seeds = getSeedTimet(2);
nanahira's avatar
nanahira committed
1750
        for (i = j = 0; j < 2; i = ++j) {
nanahira's avatar
nanahira committed
1751
          param.push(seeds[i]);
nanahira's avatar
nanahira committed
1752 1753
        }
      } else {
nanahira's avatar
nanahira committed
1754
        seeds = getSeedTimet(3);
nanahira's avatar
nanahira committed
1755
        for (i = l = 0; l < 3; i = ++l) {
nanahira's avatar
nanahira committed
1756
          param.push(seeds[i]);
nanahira's avatar
nanahira committed
1757
        }
nanahira's avatar
nanahira committed
1758
      }
mercury233's avatar
mercury233 committed
1759 1760
      try {
        this.process = spawn('./ygopro', param, {
mercury233's avatar
mercury233 committed
1761
          cwd: 'ygopro'
mercury233's avatar
mercury233 committed
1762
        });
nanahira's avatar
nanahira committed
1763
        this.process_pid = this.process.pid;
nanahira's avatar
nanahira committed
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775
        this.process.on('error', (err) => {
          _.each(this.players, function(player) {
            return ygopro.stoc_die(player, "${create_room_failed}");
          });
          this.delete();
        });
        this.process.on('exit', (code) => {
          if (!this.disconnector) {
            this.disconnector = 'server';
          }
          this.delete();
        });
mercury233's avatar
mercury233 committed
1776
        this.process.stdout.setEncoding('utf8');
nanahira's avatar
nanahira committed
1777 1778 1779 1780 1781 1782 1783 1784
        this.process.stdout.once('data', (data) => {
          this.established = true;
          if (!this.windbot && settings.modules.http.websocket_roomlist) {
            roomlist.create(this);
          }
          this.port = parseInt(data);
          _.each(this.players, (player) => {
            player.server.connect(this.port, '127.0.0.1', function() {
nanahira's avatar
nanahira committed
1785 1786 1787 1788
              var buffer, len, m, ref;
              ref = player.pre_establish_buffers;
              for (m = 0, len = ref.length; m < len; m++) {
                buffer = ref[m];
nanahira's avatar
nanahira committed
1789 1790 1791 1792
                player.server.write(buffer);
              }
              player.established = true;
              player.pre_establish_buffers = [];
mercury233's avatar
mercury233 committed
1793
            });
nanahira's avatar
nanahira committed
1794 1795 1796 1797 1798 1799 1800
          });
          if (this.windbot) {
            setTimeout(() => {
              return this.add_windbot(this.windbot);
            }, 200);
          }
        });
nanahira's avatar
fixes  
nanahira committed
1801
        return this.process.stderr.on('data', (data) => {
nanahira's avatar
nanahira committed
1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
          data = "Debug: " + data;
          data = data.replace(/\n$/, "");
          log.info("YGOPRO " + data);
          ygopro.stoc_send_chat_to_room(this, data, ygopro.constants.COLORS.RED);
          this.has_ygopro_error = true;
          this.ygopro_error_length = this.ygopro_error_length ? this.ygopro_error_length + data.length : data.length;
          if (this.ygopro_error_length > 10000) {
            this.send_replays();
            this.process.kill();
          }
        });
mercury233's avatar
mercury233 committed
1813
      } catch (error1) {
nanahira's avatar
fixes  
nanahira committed
1814
        return this.error = "${create_room_failed}";
mercury233's avatar
mercury233 committed
1815 1816 1817
      }
    }

nanahira's avatar
nanahira committed
1818
    delete() {
nanahira's avatar
nanahira committed
1819
      var end_time, formatted_replays, index, j, len, log_rep_id, name, player_datas, recorder_buffer, ref, ref1, repbuf, replay_id, room_name, score, score_array, score_form;
mercury233's avatar
mercury233 committed
1820 1821 1822
      if (this.deleted) {
        return;
      }
nanahira's avatar
nanahira committed
1823
      //log.info 'room-delete', this.name, ROOM_all.length
1824
      score_array = [];
nanahira's avatar
nanahira committed
1825 1826 1827
      ref = this.scores;
      for (name in ref) {
        score = ref[name];
nanahira's avatar
nanahira committed
1828
        score_form = {
1829
          name: name.split('$')[0],
nanahira's avatar
nanahira committed
1830
          score: score,
1831 1832
          deck: null,
          name_vpass: name
nanahira's avatar
nanahira committed
1833 1834 1835 1836 1837
        };
        if (this.decks[name]) {
          score_form.deck = this.decks[name];
        }
        score_array.push(score_form);
1838
      }
1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850
      if (settings.modules.random_duel.record_match_scores && this.random_type === 'M') {
        if (score_array.length === 2) {
          if (score_array[0].score !== score_array[1].score) {
            if (score_array[0].score > score_array[1].score) {
              ROOM_player_win(score_array[0].name_vpass);
              ROOM_player_lose(score_array[1].name_vpass);
            } else {
              ROOM_player_win(score_array[1].name_vpass);
              ROOM_player_lose(score_array[0].name_vpass);
            }
          }
        }
nanahira's avatar
nanahira committed
1851 1852
        if (score_array.length === 1) { // same name
          //log.info score_array[0].name
1853 1854 1855 1856
          ROOM_player_win(score_array[0].name_vpass);
          ROOM_player_lose(score_array[0].name_vpass);
        }
      }
nanahira's avatar
-5  
nanahira committed
1857
      if (settings.modules.arena_mode.enabled && this.arena) {
nanahira's avatar
nanahira committed
1858
        //log.info 'SCORE', score_array, @start_time
nanahira's avatar
-5  
nanahira committed
1859 1860 1861 1862 1863 1864 1865
        end_time = moment().format();
        if (!this.start_time) {
          this.start_time = end_time;
        }
        if (score_array.length !== 2) {
          if (!score_array[0]) {
            score_array[0] = {
nanahira's avatar
nanahira committed
1866
              name: null,
nanahira's avatar
nanahira committed
1867 1868
              score: -5,
              deck: null
nanahira's avatar
-5  
nanahira committed
1869
            };
1870
          }
nanahira's avatar
-5  
nanahira committed
1871 1872
          if (!score_array[1]) {
            score_array[1] = {
nanahira's avatar
nanahira committed
1873
              name: null,
nanahira's avatar
nanahira committed
1874 1875
              score: -5,
              deck: null
mercury233's avatar
merge  
mercury233 committed
1876
            };
nanahira's avatar
-5  
nanahira committed
1877 1878 1879
          }
          score_array[0].score = -5;
          score_array[1].score = -5;
mercury233's avatar
merge  
mercury233 committed
1880
        }
nanahira's avatar
fix  
nanahira committed
1881
        formatted_replays = [];
nanahira's avatar
nanahira committed
1882 1883 1884
        ref1 = this.replays;
        for (j = 0, len = ref1.length; j < len; j++) {
          repbuf = ref1[j];
nanahira's avatar
fix  
nanahira committed
1885 1886 1887 1888
          if (repbuf) {
            formatted_replays.push(repbuf.toString("base64"));
          }
        }
nanahira's avatar
-5  
nanahira committed
1889 1890 1891 1892 1893 1894 1895 1896
        request.post({
          url: settings.modules.arena_mode.post_score,
          form: {
            accesskey: settings.modules.arena_mode.accesskey,
            usernameA: score_array[0].name,
            usernameB: score_array[1].name,
            userscoreA: score_array[0].score,
            userscoreB: score_array[1].score,
nanahira's avatar
nanahira committed
1897 1898
            userdeckA: score_array[0].deck,
            userdeckB: score_array[1].deck,
nanahira's avatar
nanahira committed
1899 1900
            first: JSON.stringify(this.first_list),
            replays: JSON.stringify(formatted_replays),
nanahira's avatar
-5  
nanahira committed
1901 1902 1903 1904
            start: this.start_time,
            end: end_time,
            arena: this.arena
          }
nanahira's avatar
nanahira committed
1905 1906 1907 1908 1909 1910
        }, (error, response, body) => {
          if (error) {
            log.warn('SCORE POST ERROR', error);
          } else {
            if (response.statusCode !== 204 && response.statusCode !== 200) {
              log.warn('SCORE POST FAIL', response.statusCode, response.statusMessage, this.name, body);
nanahira's avatar
-5  
nanahira committed
1911
            }
nanahira's avatar
nanahira committed
1912 1913
          }
        });
mercury233's avatar
mercury233 committed
1914
      }
nanahira's avatar
nanahira committed
1915 1916
      //else
      //  log.info 'SCORE POST OK', response.statusCode, response.statusMessage, @name, body
nanahira's avatar
nanahira committed
1917
      if (settings.modules.challonge.enabled && this.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && this.hostinfo.mode !== 2 && !this.kicked) {
nanahira's avatar
js  
nanahira committed
1918
        room_name = this.name;
nanahira's avatar
nanahira committed
1919
        challonge.matches._update({
1920
          id: settings.modules.challonge.tournament_id,
nanahira's avatar
nanahira committed
1921
          matchId: this.challonge_info.id,
nanahira's avatar
nanahira committed
1922
          match: this.get_challonge_score(),
nanahira's avatar
nanahira committed
1923 1924
          callback: function(err, data) {
            if (err) {
nanahira's avatar
js  
nanahira committed
1925
              log.warn("Errored pushing scores to Challonge.", room_name, err);
nanahira's avatar
nanahira committed
1926 1927
            } else {
              refresh_challonge_cache();
nanahira's avatar
nanahira committed
1928 1929 1930 1931
            }
          }
        });
      }
mercury233's avatar
mercury233 committed
1932
      if (this.player_datas.length && settings.modules.cloud_replay.enabled) {
1933
        replay_id = this.cloud_replay_id;
mercury233's avatar
mercury233 committed
1934 1935 1936
        if (this.has_ygopro_error) {
          log_rep_id = true;
        }
mercury233's avatar
mercury233 committed
1937
        recorder_buffer = Buffer.concat(this.recorder_buffers);
nanahira's avatar
nanahira committed
1938
        player_datas = this.player_datas;
mercury233's avatar
mercury233 committed
1939
        zlib.deflate(recorder_buffer, function(err, replay_buffer) {
nanahira's avatar
nanahira committed
1940 1941
          dataManager.saveCloudReplay(replay_id, replay_buffer, player_datas).catch(function(err) {
            return log.warn(`Replay save error: R#${replay_id} ${err.toString()}`);
mercury233's avatar
mercury233 committed
1942
          });
mercury233's avatar
mercury233 committed
1943
          if (log_rep_id) {
mercury233's avatar
mercury233 committed
1944 1945
            log.info("error replay: R#" + replay_id);
          }
mercury233's avatar
mercury233 committed
1946 1947 1948 1949 1950 1951
        });
      }
      this.watcher_buffers = [];
      this.recorder_buffers = [];
      this.players = [];
      if (this.watcher) {
mercury233's avatar
mercury233 committed
1952
        this.watcher.destroy();
mercury233's avatar
mercury233 committed
1953
      }
mercury233's avatar
fix  
mercury233 committed
1954 1955 1956
      if (this.recorder) {
        this.recorder.destroy();
      }
mercury233's avatar
mercury233 committed
1957 1958
      this.deleted = true;
      index = _.indexOf(ROOM_all, this);
nanahira's avatar
nanahira committed
1959
      if (settings.modules.reconnect.enabled) {
nanahira's avatar
nanahira committed
1960
        ROOM_clear_disconnect(index);
nanahira's avatar
nanahira committed
1961
      }
mercury233's avatar
mercury233 committed
1962
      if (index !== -1) {
1963
        ROOM_all[index] = null;
mercury233's avatar
mercury233 committed
1964
      }
mercury233's avatar
mercury233 committed
1965
      if (!this.windbot && this.established && settings.modules.http.websocket_roomlist) {
nanahira's avatar
nanahira committed
1966 1967
        //ROOM_all.splice(index, 1) unless index == -1
        roomlist.delete(this);
mercury233's avatar
mercury233 committed
1968
      }
nanahira's avatar
nanahira committed
1969
    }
mercury233's avatar
mercury233 committed
1970

nanahira's avatar
fixes  
nanahira committed
1971
    async initialize_recover() {
nanahira's avatar
nanahira committed
1972
      var e;
nanahira's avatar
fixes  
nanahira committed
1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
      this.recover_duel_log = (await dataManager.getDuelLogFromId(this.recover_duel_log_id));
      if (!this.recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replayFileName)) {
        this.terminate();
        return false;
      }
      try {
        this.recover_replay = (await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replayFileName));
        this.spawn(this.recover_replay.header.seed);
        return true;
      } catch (error1) {
        e = error1;
        log.warn("LOAD RECOVER REPLAY FAIL", e.toString());
        this.terminate();
        return false;
      }
    }

nanahira's avatar
nanahira committed
1990
    get_playing_player() {
mercury233's avatar
mercury233 committed
1991 1992 1993 1994 1995 1996 1997 1998
      var playing_player;
      playing_player = [];
      _.each(this.players, function(player) {
        if (player.pos < 4) {
          playing_player.push(player);
        }
      });
      return playing_player;
nanahira's avatar
nanahira committed
1999
    }
mercury233's avatar
mercury233 committed
2000

nanahira's avatar
nanahira committed
2001
    get_host() {
mercury233's avatar
mercury233 committed
2002 2003 2004 2005 2006 2007 2008 2009
      var host_player;
      host_player = null;
      _.each(this.players, function(player) {
        if (player.is_host) {
          host_player = player;
        }
      });
      return host_player;
nanahira's avatar
nanahira committed
2010
    }
mercury233's avatar
mercury233 committed
2011

nanahira's avatar
nanahira committed
2012
    get_disconnected_count() {
nanahira's avatar
nanahira committed
2013
      var found, j, len, player, ref;
2014
      if (!settings.modules.reconnect.enabled) {
2015
        return 0;
2016
      }
2017
      found = 0;
nanahira's avatar
nanahira committed
2018 2019 2020
      ref = this.get_playing_player();
      for (j = 0, len = ref.length; j < len; j++) {
        player = ref[j];
2021 2022
        if (player.closed) {
          found++;
2023 2024 2025
        }
      }
      return found;
nanahira's avatar
nanahira committed
2026
    }
2027

nanahira's avatar
nanahira committed
2028
    get_challonge_score() {
nanahira's avatar
nanahira committed
2029
      var challonge_duel_log;
nanahira's avatar
nanahira committed
2030
      if (!settings.modules.challonge.enabled || this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || this.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
2031 2032 2033
        return null;
      }
      challonge_duel_log = {};
2034
      if (this.scores[this.dueling_players[0].name_vpass] > this.scores[this.dueling_players[1].name_vpass]) {
nanahira's avatar
nanahira committed
2035
        challonge_duel_log.winnerId = this.dueling_players[0].challonge_info.id;
2036
      } else if (this.scores[this.dueling_players[0].name_vpass] < this.scores[this.dueling_players[1].name_vpass]) {
nanahira's avatar
nanahira committed
2037 2038 2039 2040 2041 2042
        challonge_duel_log.winnerId = this.dueling_players[1].challonge_info.id;
      } else {
        challonge_duel_log.winnerId = "tie";
      }
      if (settings.modules.challonge.post_detailed_score) {
        if (this.dueling_players[0].challonge_info.id === this.challonge_info.player1Id && this.dueling_players[1].challonge_info.id === this.challonge_info.player2Id) {
2043
          challonge_duel_log.scoresCsv = this.scores[this.dueling_players[0].name_vpass] + "-" + this.scores[this.dueling_players[1].name_vpass];
nanahira's avatar
nanahira committed
2044
        } else if (this.dueling_players[1].challonge_info.id === this.challonge_info.player1Id && this.dueling_players[0].challonge_info.id === this.challonge_info.player2Id) {
2045
          challonge_duel_log.scoresCsv = this.scores[this.dueling_players[1].name_vpass] + "-" + this.scores[this.dueling_players[0].name_vpass];
nanahira's avatar
nanahira committed
2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059
        } else {
          challonge_duel_log.scoresCsv = "0-0";
          log.warn("Score mismatch.", this.name);
        }
      } else {
        if (challonge_duel_log.winnerId === this.challonge_info.player1Id) {
          challonge_duel_log.scoresCsv = "1-0";
        } else if (challonge_duel_log.winnerId === this.challonge_info.player2Id) {
          challonge_duel_log.scoresCsv = "0-1";
        } else {
          challonge_duel_log.scoresCsv = "0-0";
        }
      }
      return challonge_duel_log;
nanahira's avatar
nanahira committed
2060
    }
nanahira's avatar
nanahira committed
2061

nanahira's avatar
nanahira committed
2062
    get_old_hostinfo() { // Just for supporting websocket roomlist in old MyCard client....
nanahira's avatar
nanahira committed
2063 2064
      var ret;
      ret = _.clone(this.hostinfo);
nanahira's avatar
nanahira committed
2065
      ret.enable_priority = this.hostinfo.duel_rule !== 5;
nanahira's avatar
nanahira committed
2066
      return ret;
nanahira's avatar
nanahira committed
2067
    }
nanahira's avatar
nanahira committed
2068

nanahira's avatar
nanahira committed
2069
    send_replays() {
nanahira's avatar
nanahira committed
2070
      var j, l, len, len1, player, ref, ref1;
nanahira's avatar
nanahira committed
2071 2072 2073
      if (!(settings.modules.replay_delay && this.replays.length && this.hostinfo.mode === 1)) {
        return false;
      }
nanahira's avatar
nanahira committed
2074 2075 2076
      ref = this.players;
      for (j = 0, len = ref.length; j < len; j++) {
        player = ref[j];
nanahira's avatar
nanahira committed
2077 2078 2079
        if (player) {
          CLIENT_send_replays(player, this);
        }
nanahira's avatar
nanahira committed
2080
      }
nanahira's avatar
nanahira committed
2081 2082 2083
      ref1 = this.watchers;
      for (l = 0, len1 = ref1.length; l < len1; l++) {
        player = ref1[l];
nanahira's avatar
nanahira committed
2084 2085 2086
        if (player) {
          CLIENT_send_replays(player, this);
        }
nanahira's avatar
nanahira committed
2087 2088
      }
      return true;
nanahira's avatar
nanahira committed
2089
    }
nanahira's avatar
nanahira committed
2090

nanahira's avatar
nanahira committed
2091
    add_windbot(botdata) {
mercury233's avatar
mercury233 committed
2092 2093
      this.windbot = botdata;
      request({
nanahira's avatar
nanahira committed
2094 2095 2096 2097 2098 2099 2100 2101
        url: `http://${settings.modules.windbot.server_ip}:${settings.modules.windbot.port}/?name=${encodeURIComponent(botdata.name)}&deck=${encodeURIComponent(botdata.deck)}&host=${settings.modules.windbot.my_ip}&port=${settings.port}&dialog=${encodeURIComponent(botdata.dialog)}&version=${settings.version}&password=${encodeURIComponent(this.name)}`
      }, (error, response, body) => {
        if (error) {
          log.warn('windbot add error', error, this.name);
          ygopro.stoc_send_chat_to_room(this, "${add_windbot_failed}", ygopro.constants.COLORS.RED);
        }
      });
    }
mercury233's avatar
mercury233 committed
2102

nanahira's avatar
nanahira committed
2103 2104 2105
    //else
    //log.info "windbot added"
    connect(client) {
mercury233's avatar
mercury233 committed
2106
      var host_player;
mercury233's avatar
mercury233 committed
2107
      this.players.push(client);
nanahira's avatar
nanahira committed
2108
      client.join_time = moment();
mercury233's avatar
mercury233 committed
2109
      if (this.random_type) {
mercury233's avatar
mercury233 committed
2110
        client.abuse_count = 0;
mercury233's avatar
mercury233 committed
2111 2112
        host_player = this.get_host();
        if (host_player && (host_player !== client)) {
nanahira's avatar
nanahira committed
2113
          // 进来时已经有人在等待了,互相记录为匹配过
mercury233's avatar
mercury233 committed
2114 2115
          ROOM_players_oppentlist[host_player.ip] = client.ip;
          ROOM_players_oppentlist[client.ip] = host_player.ip;
mercury233's avatar
mercury233 committed
2116
        } else {
nanahira's avatar
nanahira committed
2117
          // 第一个玩家刚进来,还没就位
mercury233's avatar
mercury233 committed
2118
          ROOM_players_oppentlist[client.ip] = null;
mercury233's avatar
mercury233 committed
2119 2120 2121
        }
      }
      if (this.established) {
nanahira's avatar
nanahira committed
2122
        if (!this.windbot && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.http.websocket_roomlist) {
mercury233's avatar
mercury233 committed
2123 2124 2125
          roomlist.update(this);
        }
        client.server.connect(this.port, '127.0.0.1', function() {
nanahira's avatar
nanahira committed
2126 2127 2128 2129
          var buffer, j, len, ref;
          ref = client.pre_establish_buffers;
          for (j = 0, len = ref.length; j < len; j++) {
            buffer = ref[j];
mercury233's avatar
mercury233 committed
2130 2131 2132 2133 2134 2135
            client.server.write(buffer);
          }
          client.established = true;
          client.pre_establish_buffers = [];
        });
      }
nanahira's avatar
nanahira committed
2136
    }
mercury233's avatar
mercury233 committed
2137

nanahira's avatar
nanahira committed
2138
    disconnect(client, error) {
nanahira's avatar
nanahira committed
2139
      var index, j, l, left_name, len, len1, player, ref, ref1;
nanahira's avatar
nanahira committed
2140 2141 2142
      if (client.had_new_reconnection) {
        return;
      }
mercury233's avatar
mercury233 committed
2143
      if (client.is_post_watcher) {
nanahira's avatar
nanahira committed
2144
        ygopro.stoc_send_chat_to_room(this, `${client.name} \${quit_watch}` + (error ? `: ${error}` : ''));
mercury233's avatar
mercury233 committed
2145 2146 2147 2148
        index = _.indexOf(this.watchers, client);
        if (index !== -1) {
          this.watchers.splice(index, 1);
        }
nanahira's avatar
nanahira committed
2149
        //client.room = null
nanahira's avatar
nanahira committed
2150
        SERVER_kick(client.server);
mercury233's avatar
mercury233 committed
2151
      } else {
nanahira's avatar
nanahira committed
2152
        //log.info(client.name, @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN, @disconnector, @random_type, @players.length)
nanahira's avatar
nanahira committed
2153
        if (this.arena && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && this.disconnector !== 'server' && !this.arena_score_handled) {
2154
          if (settings.modules.arena_mode.punish_quit_before_match && this.players.length === 2 && !client.arena_quit_free) {
nanahira's avatar
nanahira committed
2155 2156 2157
            ref = this.players;
            for (j = 0, len = ref.length; j < len; j++) {
              player = ref[j];
2158 2159 2160
              if (player.pos !== 7) {
                this.scores[player.name_vpass] = 0;
              }
2161
            }
2162
            this.scores[client.name_vpass] = -9;
2163
          } else {
nanahira's avatar
nanahira committed
2164 2165 2166
            ref1 = this.players;
            for (l = 0, len1 = ref1.length; l < len1; l++) {
              player = ref1[l];
2167 2168 2169 2170
              if (player.pos !== 7) {
                this.scores[player.name_vpass] = -5;
              }
            }
nanahira's avatar
nanahira committed
2171
            if (this.players.length === 2 && !client.arena_quit_free) {
nanahira's avatar
nanahira committed
2172 2173
              this.scores[client.name_vpass] = -9;
            }
nanahira's avatar
-5  
nanahira committed
2174
          }
nanahira's avatar
nanahira committed
2175
          this.arena_score_handled = true;
2176
        }
mercury233's avatar
mercury233 committed
2177 2178 2179 2180
        index = _.indexOf(this.players, client);
        if (index !== -1) {
          this.players.splice(index, 1);
        }
nanahira's avatar
nanahira committed
2181
        if (this.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && this.disconnector !== 'server' && client.pos < 4) {
2182
          this.finished = true;
nanahira's avatar
nanahira committed
2183
          if (!this.finished_by_death) {
2184
            this.scores[client.name_vpass] = -9;
nanahira's avatar
fix  
nanahira committed
2185
            if (this.random_type && !client.flee_free && (!settings.modules.reconnect.enabled || this.get_disconnected_count() === 0)) {
nanahira's avatar
nanahira committed
2186
              ROOM_ban_player(client.name, client.ip, "${random_ban_reason_flee}");
mercury233's avatar
mercury233 committed
2187 2188 2189
              if (settings.modules.random_duel.record_match_scores && this.random_type === 'M') {
                ROOM_player_flee(client.name_vpass);
              }
nanahira's avatar
nanahira committed
2190
            }
mercury233's avatar
mercury233 committed
2191
          }
mercury233's avatar
mercury233 committed
2192
        }
nanahira's avatar
nanahira committed
2193 2194
        if (this.players.length && !(this.windbot && client.is_host) && !(this.arena && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && client.pos <= 3)) {
          left_name = (settings.modules.hide_name && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN ? "********" : client.name);
nanahira's avatar
nanahira committed
2195
          ygopro.stoc_send_chat_to_room(this, `${left_name} \${left_game}` + (error ? `: ${error}` : ''));
nanahira's avatar
nanahira committed
2196
          if (!this.windbot && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.http.websocket_roomlist) {
mercury233's avatar
mercury233 committed
2197 2198 2199
            roomlist.update(this);
          }
        } else {
nanahira's avatar
nanahira committed
2200
          //client.room = null
nanahira's avatar
nanahira committed
2201
          this.send_replays();
mercury233's avatar
mercury233 committed
2202
          this.process.kill();
nanahira's avatar
nanahira committed
2203 2204
          //client.room = null
          this.delete();
mercury233's avatar
mercury233 committed
2205
        }
nanahira's avatar
nanahira committed
2206
        if (!CLIENT_reconnect_unregister(client, false, true)) {
nanahira's avatar
nanahira committed
2207
          SERVER_kick(client.server);
nanahira's avatar
nanahira committed
2208
        }
mercury233's avatar
mercury233 committed
2209
      }
nanahira's avatar
nanahira committed
2210
    }
mercury233's avatar
mercury233 committed
2211

nanahira's avatar
nanahira committed
2212
    start_death() {
nanahira's avatar
nanahira committed
2213
      var oppo_pos, win_pos;
nanahira's avatar
nanahira committed
2214
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || this.death) {
nanahira's avatar
nanahira committed
2215 2216 2217
        return false;
      }
      oppo_pos = this.hostinfo.mode === 2 ? 2 : 1;
nanahira's avatar
nanahira committed
2218
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
nanahira's avatar
nanahira committed
2219 2220 2221 2222 2223 2224 2225
        switch (settings.modules.http.quick_death_rule) {
          case 3:
            this.death = -2;
            ygopro.stoc_send_chat_to_room(this, "${death_start_phase}", ygopro.constants.COLORS.BABYBLUE);
            break;
          default:
            this.death = (this.turn ? this.turn + 4 : 5);
nanahira's avatar
nanahira committed
2226
            ygopro.stoc_send_chat_to_room(this, "${death_start}", ygopro.constants.COLORS.BABYBLUE); // Extra duel started in siding
nanahira's avatar
nanahira committed
2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267
        }
      } else {
        switch (settings.modules.http.quick_death_rule) {
          case 2:
          case 3:
            if (this.scores[this.dueling_players[0].name_vpass] === this.scores[this.dueling_players[oppo_pos].name_vpass]) {
              if (settings.modules.http.quick_death_rule === 3) {
                this.death = -1;
                ygopro.stoc_send_chat_to_room(this, "${death_start_quick}", ygopro.constants.COLORS.BABYBLUE);
              } else {
                this.death = 5;
                ygopro.stoc_send_chat_to_room(this, "${death_start_siding}", ygopro.constants.COLORS.BABYBLUE);
              }
            } else {
              win_pos = this.scores[this.dueling_players[0].name_vpass] > this.scores[this.dueling_players[oppo_pos].name_vpass] ? 0 : oppo_pos;
              this.finished_by_death = true;
              ygopro.stoc_send_chat_to_room(this, "${death2_finish_part1}" + this.dueling_players[win_pos].name + "${death2_finish_part2}", ygopro.constants.COLORS.BABYBLUE);
              if (this.hostinfo.mode === 1) {
                CLIENT_send_replays(this.dueling_players[oppo_pos - win_pos], this);
              }
              ygopro.stoc_send(this.dueling_players[oppo_pos - win_pos], 'DUEL_END');
              if (this.hostinfo.mode === 2) {
                ygopro.stoc_send(this.dueling_players[oppo_pos - win_pos + 1], 'DUEL_END');
              }
              this.scores[this.dueling_players[oppo_pos - win_pos].name_vpass] = -1;
              CLIENT_kick(this.dueling_players[oppo_pos - win_pos]);
              if (this.hostinfo.mode === 2) {
                CLIENT_kick(this.dueling_players[oppo_pos - win_pos + 1]);
              }
            }
            break;
          case 1:
            this.death = -1;
            ygopro.stoc_send_chat_to_room(this, "${death_start_quick}", ygopro.constants.COLORS.BABYBLUE);
            break;
          default:
            this.death = 5;
            ygopro.stoc_send_chat_to_room(this, "${death_start_siding}", ygopro.constants.COLORS.BABYBLUE);
        }
      }
      return true;
nanahira's avatar
nanahira committed
2268
    }
nanahira's avatar
nanahira committed
2269

nanahira's avatar
nanahira committed
2270
    cancel_death() {
nanahira's avatar
nanahira committed
2271
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || !this.death) {
nanahira's avatar
nanahira committed
2272 2273 2274 2275 2276
        return false;
      }
      this.death = 0;
      ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE);
      return true;
nanahira's avatar
nanahira committed
2277
    }
mercury233's avatar
mercury233 committed
2278

nanahira's avatar
fixes  
nanahira committed
2279
    terminate() {
nanahira's avatar
nanahira committed
2280
      var e;
nanahira's avatar
nanahira committed
2281 2282 2283 2284 2285 2286
      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();
nanahira's avatar
fixes  
nanahira committed
2287 2288 2289 2290 2291 2292 2293
      if (this.process) {
        try {
          this.process.kill();
        } catch (error1) {
          e = error1;
        }
      }
nanahira's avatar
nanahira committed
2294 2295 2296 2297
      return this.delete();
    }

    finish_recover(fail) {
nanahira's avatar
nanahira committed
2298
      var buffer, j, len, player, ref, results;
nanahira's avatar
nanahira committed
2299 2300
      if (fail) {
        ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED);
nanahira's avatar
fixes  
nanahira committed
2301
        return this.terminate();
nanahira's avatar
nanahira committed
2302 2303 2304
      } else {
        ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE);
        this.recovering = false;
nanahira's avatar
nanahira committed
2305
        ref = this.get_playing_player();
nanahira's avatar
nanahira committed
2306
        results = [];
nanahira's avatar
nanahira committed
2307 2308
        for (j = 0, len = ref.length; j < len; j++) {
          player = ref[j];
nanahira's avatar
nanahira committed
2309
          results.push((function() {
nanahira's avatar
nanahira committed
2310 2311
            var l, len1, ref1, results1;
            ref1 = this.recover_buffers[player.pos];
nanahira's avatar
nanahira committed
2312
            results1 = [];
nanahira's avatar
nanahira committed
2313 2314
            for (l = 0, len1 = ref1.length; l < len1; l++) {
              buffer = ref1[l];
nanahira's avatar
nanahira committed
2315 2316 2317 2318 2319 2320 2321 2322 2323
              results1.push(ygopro.stoc_send(player, "GAME_MSG", buffer));
            }
            return results1;
          }).call(this));
        }
        return results;
      }
    }

nanahira's avatar
nanahira committed
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343
    async check_athletic() {
      var players, room;
      players = this.get_playing_player();
      room = this;
      await Promise.all(players.map(async function(player) {
        var main, side, using_athletic;
        main = _.clone(player.main);
        side = _.clone(player.side);
        using_athletic = (await athleticChecker.checkAthletic({
          main: main,
          side: side
        }));
        if (!using_athletic.success) {
          return log.warn("GET ATHLETIC FAIL", player.name, using_athletic.message);
        } else if (using_athletic.athletic) {
          return ygopro.stoc_send_chat_to_room(room, `${player.name}\${using_athletic_deck}`, ygopro.constants.COLORS.BABYBLUE);
        }
      }));
    }

nanahira's avatar
nanahira committed
2344
  };
mercury233's avatar
mercury233 committed
2345

nanahira's avatar
nanahira committed
2346
  // 网络连接
nanahira's avatar
nanahira committed
2347
  netRequestHandler = function(client) {
mercury233's avatar
mercury233 committed
2348 2349
    var connect_count, server;
    client.ip = client.remoteAddress;
nanahira's avatar
nanahira committed
2350
    client.is_local = client.ip && (client.ip.includes('127.0.0.1') || client.ip.includes(real_windbot_server_ip));
mercury233's avatar
mercury233 committed
2351
    connect_count = ROOM_connected_ip[client.ip] || 0;
2352
    if (!settings.modules.test_mode.no_connect_count_limit && !client.is_local) {
mercury233's avatar
mercury233 committed
2353 2354 2355
      connect_count++;
    }
    ROOM_connected_ip[client.ip] = connect_count;
nanahira's avatar
nanahira committed
2356 2357 2358
    //log.info "connect", client.ip, ROOM_connected_ip[client.ip]

    // server stand for the connection to ygopro server process
神楽坂玲奈's avatar
神楽坂玲奈 committed
2359 2360
    server = new net.Socket();
    client.server = server;
nanahira's avatar
nanahira committed
2361
    server.client = client;
nanahira's avatar
nanahira committed
2362 2363 2364
    client.setTimeout(2000); //连接前超时2秒
    
    // 释放处理
2365
    client.on('close', function(had_error) {
mercury233's avatar
mercury233 committed
2366
      var room;
nanahira's avatar
nanahira committed
2367
      //log.info "client closed", client.name, had_error
2368
      room = ROOM_all[client.rid];
mercury233's avatar
mercury233 committed
2369 2370 2371 2372 2373
      connect_count = ROOM_connected_ip[client.ip];
      if (connect_count > 0) {
        connect_count--;
      }
      ROOM_connected_ip[client.ip] = connect_count;
nanahira's avatar
nanahira committed
2374
      //log.info "disconnect", client.ip, ROOM_connected_ip[client.ip]
2375
      if (!client.closed) {
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2376
        client.closed = true;
nanahira's avatar
nanahira committed
2377 2378 2379
        if (settings.modules.heartbeat_detection.enabled) {
          CLIENT_heartbeat_unregister(client);
        }
2380
        if (room) {
nanahira's avatar
nanahira committed
2381
          if (!CLIENT_reconnect_register(client, client.rid)) {
nanahira's avatar
nanahira committed
2382 2383 2384
            room.disconnect(client);
          }
        } else if (!client.had_new_reconnection) {
nanahira's avatar
nanahira committed
2385
          SERVER_kick(client.server);
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2386
        }
mercury233's avatar
mercury233 committed
2387 2388
      }
    });
2389
    client.on('error', function(error) {
mercury233's avatar
mercury233 committed
2390
      var room;
nanahira's avatar
nanahira committed
2391
      //log.info "client error", client.name, error
2392
      room = ROOM_all[client.rid];
mercury233's avatar
mercury233 committed
2393 2394 2395 2396 2397
      connect_count = ROOM_connected_ip[client.ip];
      if (connect_count > 0) {
        connect_count--;
      }
      ROOM_connected_ip[client.ip] = connect_count;
nanahira's avatar
nanahira committed
2398
      //log.info "err disconnect", client.ip, ROOM_connected_ip[client.ip]
2399
      if (!client.closed) {
nanahira's avatar
nanahira committed
2400
        client.closed = true;
2401
        if (room) {
nanahira's avatar
nanahira committed
2402
          if (!CLIENT_reconnect_register(client, client.rid, error)) {
nanahira's avatar
nanahira committed
2403 2404 2405
            room.disconnect(client, error);
          }
        } else if (!client.had_new_reconnection) {
nanahira's avatar
nanahira committed
2406
          SERVER_kick(client.server);
2407
        }
神楽坂玲奈's avatar
神楽坂玲奈 committed
2408
      }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2409
    });
2410
    client.on('timeout', function() {
nanahira's avatar
nanahira committed
2411
      if (!(settings.modules.reconnect.enabled && (disconnect_list[CLIENT_get_authorize_key(client)] || client.had_new_reconnection))) {
nanahira's avatar
nanahira committed
2412
        client.destroy();
nanahira's avatar
nanahira committed
2413
      }
2414
    });
2415
    server.on('close', function(had_error) {
2416
      var room;
2417 2418
      if (!server.closed) {
        server.closed = true;
mercury233's avatar
mercury233 committed
2419
      }
nanahira's avatar
nanahira committed
2420 2421 2422
      if (!server.client) {
        return;
      }
nanahira's avatar
nanahira committed
2423
      //log.info "server closed", server.client.name, had_error
nanahira's avatar
nanahira committed
2424
      room = ROOM_all[server.client.rid];
nanahira's avatar
nanahira committed
2425
      if (room && !server.system_kicked && !server.had_new_reconnection) {
nanahira's avatar
nanahira committed
2426
        //log.info "server close", server.client.ip, ROOM_connected_ip[server.client.ip]
nanahira's avatar
nanahira committed
2427 2428
        room.disconnector = 'server';
      }
nanahira's avatar
rev  
nanahira committed
2429
      if (!server.client.closed) {
nanahira's avatar
nanahira committed
2430
        ygopro.stoc_send_chat(server.client, "${server_closed}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
2431 2432
        //if room and settings.modules.replay_delay
        //  room.send_replays()
nanahira's avatar
nanahira committed
2433 2434
        CLIENT_kick(server.client);
        SERVER_clear_disconnect(server);
mercury233's avatar
mercury233 committed
2435 2436
      }
    });
2437
    server.on('error', function(error) {
2438
      var room;
2439
      server.closed = error;
nanahira's avatar
nanahira committed
2440 2441 2442
      if (!server.client) {
        return;
      }
nanahira's avatar
nanahira committed
2443
      //log.info "server error", client.name, error
nanahira's avatar
nanahira committed
2444
      room = ROOM_all[server.client.rid];
nanahira's avatar
nanahira committed
2445
      if (room && !server.system_kicked && !server.had_new_reconnection) {
nanahira's avatar
nanahira committed
2446
        //log.info "server err close", client.ip, ROOM_connected_ip[client.ip]
nanahira's avatar
nanahira committed
2447 2448
        room.disconnector = 'server';
      }
nanahira's avatar
nanahira committed
2449
      if (!server.client.closed) {
nanahira's avatar
nanahira committed
2450 2451 2452
        ygopro.stoc_send_chat(server.client, `\${server_error}: ${error}`, ygopro.constants.COLORS.RED);
        //if room and settings.modules.replay_delay
        //  room.send_replays()
nanahira's avatar
nanahira committed
2453 2454
        CLIENT_kick(server.client);
        SERVER_clear_disconnect(server);
mercury233's avatar
mercury233 committed
2455 2456
      }
    });
mercury233's avatar
mercury233 committed
2457
    if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
mercury233's avatar
mercury233 committed
2458
      log.info('BAD IP', client.ip);
nanahira's avatar
nanahira committed
2459
      CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2460 2461
      return;
    }
mercury233's avatar
mercury233 committed
2462
    if (settings.modules.cloud_replay.enabled) {
nanahira's avatar
nanahira committed
2463
      client.open_cloud_replay = async function(replay) {
nanahira's avatar
nanahira committed
2464
        var buffer, e, replay_buffer;
nanahira's avatar
nanahira committed
2465
        if (!replay) {
mercury233's avatar
mercury233 committed
2466
          ygopro.stoc_die(client, "${cloud_replay_no}");
mercury233's avatar
mercury233 committed
2467
          return;
mercury233's avatar
mercury233 committed
2468
        }
nanahira's avatar
nanahira committed
2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481
        buffer = replay.toBuffer();
        replay_buffer = null;
        try {
          replay_buffer = (await util.promisify(zlib.unzip)(buffer));
        } catch (error1) {
          e = error1;
          log.info("cloud replay unzip error: " + err);
          ygopro.stoc_die(client, "${cloud_replay_error}");
          return;
        }
        ygopro.stoc_send_chat(client, `\${cloud_replay_playing} ${replay.getDisplayString()}`, ygopro.constants.COLORS.BABYBLUE);
        client.write(replay_buffer, function() {
          CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2482
        });
mercury233's avatar
mercury233 committed
2483 2484
      };
    }
nanahira's avatar
nanahira committed
2485 2486
    // 需要重构
    // 客户端到服务端(ctos)协议分析
神楽坂玲奈's avatar
神楽坂玲奈 committed
2487
    client.pre_establish_buffers = new Array();
nanahira's avatar
nanahira committed
2488
    client.on('data', async function(ctos_buffer) {
nanahira's avatar
nanahira committed
2489
      var bad_ip_count, buffer, ctos_filter, handle_data, j, l, len, len1, len2, m, ref, ref1, ref2, room;
2490
      if (client.is_post_watcher) {
2491
        room = ROOM_all[client.rid];
2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509
        if (room) {
          handle_data = (await ygopro.helper.handleBuffer(ctos_buffer, "CTOS", ["CHAT"], {
            client: client,
            server: client.server
          }));
          if (handle_data.feedback) {
            log.warn(handle_data.feedback.message, client.name, client.ip);
            if (handle_data.feedback.type === "OVERSIZE" || ROOM_bad_ip[client.ip] > 5) {
              bad_ip_count = ROOM_bad_ip[client.ip];
              if (bad_ip_count) {
                ROOM_bad_ip[client.ip] = bad_ip_count + 1;
              } else {
                ROOM_bad_ip[client.ip] = 1;
              }
              CLIENT_kick(client);
              return;
            }
          }
nanahira's avatar
nanahira committed
2510 2511 2512
          ref = handle_data.datas;
          for (j = 0, len = ref.length; j < len; j++) {
            buffer = ref[j];
2513 2514
            room.watcher.write(buffer);
          }
2515
        }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2516
      } else {
nanahira's avatar
nanahira committed
2517 2518 2519 2520 2521 2522
        ctos_filter = settings.modules.reconnect.enabled && client.pre_reconnecting ? ["UPDATE_DECK"] : null;
        handle_data = (await ygopro.helper.handleBuffer(ctos_buffer, "CTOS", ctos_filter, {
          client: client,
          server: client.server
        }));
        if (handle_data.feedback) {
2523
          log.warn(handle_data.feedback.message, client.name, client.ip);
nanahira's avatar
nanahira committed
2524
          if (handle_data.feedback.type === "OVERSIZE" || ROOM_bad_ip[client.ip] > 5) {
mercury233's avatar
mercury233 committed
2525
            bad_ip_count = ROOM_bad_ip[client.ip];
mercury233's avatar
mercury233 committed
2526
            if (bad_ip_count) {
mercury233's avatar
mercury233 committed
2527
              ROOM_bad_ip[client.ip] = bad_ip_count + 1;
mercury233's avatar
mercury233 committed
2528
            } else {
mercury233's avatar
mercury233 committed
2529
              ROOM_bad_ip[client.ip] = 1;
mercury233's avatar
mercury233 committed
2530
            }
nanahira's avatar
nanahira committed
2531
            CLIENT_kick(client);
nanahira's avatar
nanahira committed
2532
            return;
mercury233's avatar
mercury233 committed
2533
          }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2534
        }
nanahira's avatar
nanahira committed
2535 2536 2537
        if (!client.server) {
          return;
        }
2538
        if (client.established) {
nanahira's avatar
nanahira committed
2539 2540 2541
          ref1 = handle_data.datas;
          for (l = 0, len1 = ref1.length; l < len1; l++) {
            buffer = ref1[l];
2542 2543 2544
            client.server.write(buffer);
          }
        } else {
nanahira's avatar
nanahira committed
2545 2546 2547
          ref2 = handle_data.datas;
          for (m = 0, len2 = ref2.length; m < len2; m++) {
            buffer = ref2[m];
2548 2549 2550
            client.pre_establish_buffers.push(buffer);
          }
        }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2551 2552
      }
    });
nanahira's avatar
nanahira committed
2553
    // 服务端到客户端(stoc)
nanahira's avatar
nanahira committed
2554
    server.on('data', async function(stoc_buffer) {
nanahira's avatar
nanahira committed
2555
      var buffer, handle_data, j, len, ref;
nanahira's avatar
nanahira committed
2556 2557 2558 2559 2560
      handle_data = (await ygopro.helper.handleBuffer(stoc_buffer, "STOC", null, {
        client: server.client,
        server: server
      }));
      if (handle_data.feedback) {
2561
        log.warn(handle_data.feedback.message, server.client.name, server.client.ip);
nanahira's avatar
nanahira committed
2562
        if (handle_data.feedback.type === "OVERSIZE") {
2563
          server.destroy();
nanahira's avatar
nanahira committed
2564
          return;
mercury233's avatar
mercury233 committed
2565
        }
神楽坂玲奈's avatar
 
神楽坂玲奈 committed
2566
      }
nanahira's avatar
nanahira committed
2567
      if (server.client && !server.client.closed) {
nanahira's avatar
nanahira committed
2568 2569 2570
        ref = handle_data.datas;
        for (j = 0, len = ref.length; j < len; j++) {
          buffer = ref[j];
nanahira's avatar
nanahira committed
2571 2572
          server.client.write(buffer);
        }
2573
      }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2574
    });
nanahira's avatar
nanahira committed
2575
  };
mercury233's avatar
mercury233 committed
2576

Yuzurisa's avatar
Yuzurisa committed
2577 2578 2579 2580 2581
  deck_name_match = global.deck_name_match = function(deck_name, player_name) {
    var parsed_deck_name;
    if (deck_name === player_name || deck_name === player_name + ".ydk" || deck_name === player_name + ".ydk.ydk") {
      return true;
    }
nanahira's avatar
js  
nanahira committed
2582
    parsed_deck_name = deck_name.match(/^([^\+ \uff0b]+)[\+ \uff0b](.+?)(\.ydk){0,2}$/);
Yuzurisa's avatar
Yuzurisa committed
2583 2584 2585
    return parsed_deck_name && (player_name === parsed_deck_name[1] || player_name === parsed_deck_name[2]);
  };

nanahira's avatar
nanahira committed
2586 2587
  // 功能模块
  // return true to cancel a synchronous message
nanahira's avatar
nanahira committed
2588
  ygopro.ctos_follow('PLAYER_INFO', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
2589
    var banMCRequest, e, geo, lang, name, name_full, struct, vpass;
nanahira's avatar
nanahira committed
2590 2591
    // checkmate use username$password, but here don't
    // so remove the password
nanahira's avatar
nanahira committed
2592 2593 2594 2595 2596 2597
    name_full = info.name.split("$");
    name = name_full[0];
    vpass = name_full[1];
    if (vpass && !vpass.length) {
      vpass = null;
    }
mercury233's avatar
mercury233 committed
2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609
    if (_.any(settings.ban.illegal_id, function(badid) {
      var matchs, regexp;
      regexp = new RegExp(badid, 'i');
      matchs = name.match(regexp);
      if (matchs) {
        name = matchs[1];
        return true;
      }
      return false;
    }, name)) {
      client.rag = true;
    }
2610
    if (settings.modules.mycard.enabled && settings.modules.mycard.ban_get && !client.is_local) {
nanahira's avatar
nanahira committed
2611
      try {
nanahira's avatar
fix  
nanahira committed
2612
        banMCRequest = (await axios.get(settings.modules.mycard.ban_get, {
nanahira's avatar
nanahira committed
2613 2614 2615 2616 2617 2618 2619
          paramsSerializer: qs.stringify,
          params: {
            user: name
          }
        }));
        if (typeof banMCRequest.data === "object") {
          client.ban_mc = banMCRequest.data;
nanahira's avatar
nanahira committed
2620
        } else {
nanahira's avatar
nanahira committed
2621
          log.warn("ban get bad json", banMCRequest.data);
nanahira's avatar
nanahira committed
2622
        }
nanahira's avatar
nanahira committed
2623 2624 2625 2626
      } catch (error1) {
        e = error1;
        log.warn('ban get error', e.toString());
      }
nanahira's avatar
nanahira committed
2627
    }
2628
    struct = ygopro.structs.get("CTOS_PlayerInfo");
2629 2630 2631 2632
    struct._setBuff(buffer);
    struct.set("name", name);
    buffer = struct.buffer;
    client.name = name;
nanahira's avatar
nanahira committed
2633
    client.vpass = vpass;
2634
    client.name_vpass = vpass ? name + "$" + vpass : name;
2635
    if (!settings.modules.i18n.auto_pick || client.is_local) {
nanahira's avatar
nanahira committed
2636
      client.lang = settings.modules.i18n.default;
mercury233's avatar
mercury233 committed
2637 2638 2639
    } else {
      geo = geoip.lookup(client.ip);
      if (!geo) {
mercury233's avatar
mercury233 committed
2640
        log.warn("fail to locate ip", client.name, client.ip);
mercury233's avatar
mercury233 committed
2641 2642 2643 2644 2645
        client.lang = settings.modules.i18n.fallback;
      } else {
        if (lang = settings.modules.i18n.map[geo.country]) {
          client.lang = lang;
        } else {
nanahira's avatar
nanahira committed
2646
          //log.info("Not in map", geo.country, client.name, client.ip)
mercury233's avatar
mercury233 committed
2647 2648 2649
          client.lang = settings.modules.i18n.fallback;
        }
      }
mercury233's avatar
mercury233 committed
2650
    }
2651
    return false;
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2652 2653
  });

nanahira's avatar
fix  
nanahira committed
2654
  ygopro.ctos_follow('JOIN_GAME', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
2655
    var available_logs, check_buffer_indentity, create_room_with_action, duelLog, exactBan, index, j, l, len, len1, len2, len3, m, n, name, pre_room, recover_match, ref, ref1, replay, replay_id, replays, room, struct;
nanahira's avatar
nanahira committed
2656
    //log.info info
mercury233's avatar
mercury233 committed
2657
    info.pass = info.pass.trim();
nanahira's avatar
nanahira committed
2658 2659
    client.pass = info.pass;
    if (CLIENT_is_able_to_reconnect(client) || CLIENT_is_able_to_kick_reconnect(client)) {
nanahira's avatar
nanahira committed
2660 2661 2662
      CLIENT_pre_reconnect(client);
      return;
    } else if (settings.modules.stop) {
mercury233's avatar
mercury233 committed
2663
      ygopro.stoc_die(client, settings.modules.stop);
nanahira's avatar
nanahira committed
2664 2665
    } else if (info.pass === "Marshtomp" || info.pass === "the Big Brother") {
      ygopro.stoc_die(client, "${bad_user_name}");
mercury233's avatar
mercury233 committed
2666
    } else if (info.pass.toUpperCase() === "R" && settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
2667
      ygopro.stoc_send_chat(client, "${cloud_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
2668
      replays = (await dataManager.getCloudReplaysFromKey(CLIENT_get_authorize_key(client)));
nanahira's avatar
nanahira committed
2669
      for (index = j = 0, len = replays.length; j < len; index = ++j) {
nanahira's avatar
nanahira committed
2670 2671 2672 2673 2674 2675
        replay = replays[index];
        ygopro.stoc_send_chat(client, `<${index + 1}> ${replay.getDisplayString()}`, ygopro.constants.COLORS.BABYBLUE);
      }
      ygopro.stoc_send(client, 'ERROR_MSG', {
        msg: 1,
        code: 9
mercury233's avatar
mercury233 committed
2676
      });
nanahira's avatar
nanahira committed
2677
      CLIENT_kick(client);
nanahira's avatar
nanahira committed
2678 2679
    } else if (info.pass.toUpperCase() === "RC" && settings.modules.tournament_mode.enable_recover) {
      ygopro.stoc_send_chat(client, "${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
fixes  
nanahira committed
2680
      available_logs = (await dataManager.getDuelLogFromRecoverSearch(client.name_vpass));
nanahira's avatar
nanahira committed
2681 2682
      for (l = 0, len1 = available_logs.length; l < len1; l++) {
        duelLog = available_logs[l];
nanahira's avatar
fixes  
nanahira committed
2683 2684 2685 2686 2687
        ygopro.stoc_send_chat(client, duelLog.getViewString(), ygopro.constants.COLORS.BABYBLUE);
      }
      ygopro.stoc_send(client, 'ERROR_MSG', {
        msg: 1,
        code: 9
nanahira's avatar
nanahira committed
2688
      });
nanahira's avatar
fixes  
nanahira committed
2689
      CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2690
    } else if (info.pass.slice(0, 2).toUpperCase() === "R#" && settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
2691
      replay_id = info.pass.split("#")[1];
nanahira's avatar
nanahira committed
2692 2693
      replay = (await dataManager.getCloudReplayFromId(replay_id));
      await client.open_cloud_replay(replay);
mercury233's avatar
mercury233 committed
2694
    } else if (info.pass.toUpperCase() === "W" && settings.modules.cloud_replay.enabled) {
nanahira's avatar
nanahira committed
2695 2696
      replay = (await dataManager.getRandomCloudReplay());
      await client.open_cloud_replay(replay);
2697
    } else if (info.version !== settings.version && !settings.alternative_versions.includes(info.version)) {
mercury233's avatar
mercury233 committed
2698
      ygopro.stoc_send_chat(client, settings.modules.update, ygopro.constants.COLORS.RED);
mercury233's avatar
fix  
mercury233 committed
2699 2700 2701 2702
      ygopro.stoc_send(client, 'ERROR_MSG', {
        msg: 4,
        code: settings.version
      });
nanahira's avatar
nanahira committed
2703
      CLIENT_kick(client);
nanahira's avatar
nanahira committed
2704
    } else if (!info.pass.length && !settings.modules.random_duel.enabled && !settings.modules.windbot.enabled && !settings.modules.challonge.enabled) {
mercury233's avatar
mercury233 committed
2705
      ygopro.stoc_die(client, "${blank_room_name}");
mercury233's avatar
fix  
mercury233 committed
2706
    } else if (info.pass.length && settings.modules.mycard.enabled && info.pass.slice(0, 3) !== 'AI#') {
mercury233's avatar
mercury233 committed
2707
      ygopro.stoc_send_chat(client, '${loading_user_info}', ygopro.constants.COLORS.BABYBLUE);
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2708
      if (info.pass.length <= 8) {
mercury233's avatar
mercury233 committed
2709
        ygopro.stoc_die(client, '${invalid_password_length}');
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2710 2711
        return;
      }
2712 2713 2714 2715 2716 2717 2718
      if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
        info.version = settings.version;
        struct = ygopro.structs.get("CTOS_JoinGame");
        struct._setBuff(buffer);
        struct.set("version", info.version);
        buffer = struct.buffer;
      }
nanahira's avatar
nanahira committed
2719
      buffer = Buffer.from(info.pass.slice(0, 8), 'base64');
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2720
      if (buffer.length !== 6) {
mercury233's avatar
mercury233 committed
2721
        ygopro.stoc_die(client, '${invalid_password_payload}');
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2722 2723
        return;
      }
nanahira's avatar
nanahira committed
2724
      check_buffer_indentity = function(buf) {
nanahira's avatar
nanahira committed
2725
        var checksum, i, m, ref;
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2726
        checksum = 0;
nanahira's avatar
nanahira committed
2727
        for (i = m = 0, ref = buf.length; (0 <= ref ? m < ref : m > ref); i = 0 <= ref ? ++m : --m) {
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2728
          checksum += buf.readUInt8(i);
2729
        }
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2730 2731
        return (checksum & 0xFF) === 0;
      };
nanahira's avatar
nanahira committed
2732
      create_room_with_action = async function(buffer, decrypted_buffer, match_permit) {
nanahira's avatar
nanahira committed
2733
        var action, len2, len3, m, n, name, opt1, opt2, opt3, options, player, ref, ref1, room, room_title, title;
nanahira's avatar
nanahira committed
2734 2735 2736
        if (client.closed) {
          return;
        }
2737 2738
        action = buffer.readUInt8(1) >> 4;
        if (buffer !== decrypted_buffer && (action === 1 || action === 2 || action === 4)) {
mercury233's avatar
mercury233 committed
2739
          ygopro.stoc_die(client, '${invalid_password_unauthorized}');
2740 2741
          return;
        }
nanahira's avatar
nanahira committed
2742 2743 2744 2745 2746
        // 1 create public room
        // 2 create private room
        // 3 join room by id
        // 4 create or join room by id (use for match)
        // 5 join room by title
2747 2748 2749 2750
        switch (action) {
          case 1:
          case 2:
            name = crypto.createHash('md5').update(info.pass + client.name).digest('base64').slice(0, 10).replace('+', '-').replace('/', '_');
mercury233's avatar
mercury233 committed
2751
            if (ROOM_find_by_name(name)) {
mercury233's avatar
mercury233 committed
2752
              ygopro.stoc_die(client, '${invalid_password_existed}');
2753 2754 2755
              return;
            }
            opt1 = buffer.readUInt8(2);
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2756
            opt2 = buffer.readUInt16LE(3);
2757 2758
            opt3 = buffer.readUInt8(5);
            options = {
nanahira's avatar
nanahira committed
2759 2760
              lflist: settings.hostinfo.lflist,
              time_limit: settings.hostinfo.time_limit,
2761 2762
              rule: (opt1 >> 5) & 3,
              mode: (opt1 >> 3) & 3,
nanahira's avatar
nanahira committed
2763
              duel_rule: (!!((opt1 >> 2) & 1) ? 4 : 5),
2764 2765 2766 2767
              no_check_deck: !!((opt1 >> 1) & 1),
              no_shuffle_deck: !!(opt1 & 1),
              start_lp: opt2,
              start_hand: opt3 >> 4,
nanahira's avatar
nanahira committed
2768
              draw_count: opt3 & 0xF,
nanahira's avatar
nanahira committed
2769 2770
              no_watch: settings.hostinfo.no_watch,
              auto_death: settings.hostinfo.auto_death
2771
            };
mercury233's avatar
mercury233 committed
2772
            options.lflist = _.findIndex(lflists, function(list) {
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2773
              return ((options.rule === 1) === list.tcg) && list.date.isBefore();
神楽坂玲奈's avatar
神楽坂玲奈 committed
2774
            });
nanahira's avatar
nanahira committed
2775 2776 2777 2778 2779 2780
            room_title = info.pass.slice(8).replace(String.fromCharCode(0xFEFF), ' ');
            if (_.any(badwords.level3, function(badword) {
              var regexp;
              regexp = new RegExp(badword, 'i');
              return room_title.match(regexp);
            }, room_title)) {
nanahira's avatar
fix  
nanahira committed
2781
              log.warn("BAD ROOM NAME LEVEL 3", room_title, client.name, client.ip);
nanahira's avatar
nanahira committed
2782
              ygopro.stoc_die(client, "${bad_roomname_level3}");
nanahira's avatar
nanahira committed
2783 2784 2785 2786 2787 2788
              return;
            } else if (_.any(badwords.level2, function(badword) {
              var regexp;
              regexp = new RegExp(badword, 'i');
              return room_title.match(regexp);
            }, room_title)) {
nanahira's avatar
fix  
nanahira committed
2789
              log.warn("BAD ROOM NAME LEVEL 2", room_title, client.name, client.ip);
nanahira's avatar
nanahira committed
2790
              ygopro.stoc_die(client, "${bad_roomname_level2}");
nanahira's avatar
nanahira committed
2791 2792 2793 2794 2795 2796
              return;
            } else if (_.any(badwords.level1, function(badword) {
              var regexp;
              regexp = new RegExp(badword, 'i');
              return room_title.match(regexp);
            }, room_title)) {
nanahira's avatar
fix  
nanahira committed
2797
              log.warn("BAD ROOM NAME LEVEL 1", room_title, client.name, client.ip);
nanahira's avatar
nanahira committed
2798
              ygopro.stoc_die(client, "${bad_roomname_level1}");
nanahira's avatar
nanahira committed
2799 2800
              return;
            }
2801
            room = new Room(name, options);
nanahira's avatar
nanahira committed
2802
            if (room) {
nanahira's avatar
nanahira committed
2803
              room.title = room_title;
nanahira's avatar
nanahira committed
2804
              room.private = action === 2;
nanahira's avatar
nanahira committed
2805
            }
2806 2807 2808
            break;
          case 3:
            name = info.pass.slice(8);
mercury233's avatar
mercury233 committed
2809
            room = ROOM_find_by_name(name);
2810
            if (!room) {
mercury233's avatar
mercury233 committed
2811
              ygopro.stoc_die(client, '${invalid_password_not_found}');
2812 2813 2814 2815
              return;
            }
            break;
          case 4:
nanahira's avatar
move  
nanahira committed
2816 2817 2818 2819
            if (match_permit && !match_permit.permit) {
              ygopro.stoc_die(client, '${invalid_password_unauthorized}');
              return;
            }
nanahira's avatar
nanahira committed
2820
            room = (await ROOM_find_or_create_by_name('M#' + info.pass.slice(8)));
nanahira's avatar
nanahira committed
2821
            if (room) {
nanahira's avatar
nanahira committed
2822 2823 2824
              ref = room.get_playing_player();
              for (m = 0, len2 = ref.length; m < len2; m++) {
                player = ref[m];
nanahira's avatar
nanahira committed
2825 2826 2827 2828 2829 2830
                if (!(player && player.name === client.name)) {
                  continue;
                }
                ygopro.stoc_die(client, '${invalid_password_unauthorized}');
                return;
              }
nanahira's avatar
nanahira committed
2831
              room.private = true;
nanahira's avatar
nanahira committed
2832 2833 2834 2835 2836
              room.arena = settings.modules.arena_mode.mode;
              if (room.arena === "athletic") {
                room.max_player = 2;
                room.welcome = "${athletic_arena_tip}";
              }
2837
            }
2838
            break;
mercury233's avatar
mercury233 committed
2839 2840 2841 2842 2843 2844 2845 2846
          case 5:
            title = info.pass.slice(8).replace(String.fromCharCode(0xFEFF), ' ');
            room = ROOM_find_by_title(title);
            if (!room) {
              ygopro.stoc_die(client, '${invalid_password_not_found}');
              return;
            }
            break;
2847
          default:
mercury233's avatar
mercury233 committed
2848
            ygopro.stoc_die(client, '${invalid_password_action}');
2849 2850
            return;
        }
mercury233's avatar
mercury233 committed
2851
        if (!room) {
mercury233's avatar
mercury233 committed
2852
          ygopro.stoc_die(client, "${server_full}");
mercury233's avatar
mercury233 committed
2853
        } else if (room.error) {
mercury233's avatar
mercury233 committed
2854
          ygopro.stoc_die(client, room.error);
nanahira's avatar
nanahira committed
2855
        } else if (room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
nanahira's avatar
nanahira committed
2856
          if (settings.modules.cloud_replay.enable_halfway_watch && !room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
2857
            client.setTimeout(300000); //连接后超时5分钟
mercury233's avatar
mercury233 committed
2858 2859
            client.rid = _.indexOf(ROOM_all, room);
            client.is_post_watcher = true;
nanahira's avatar
nanahira committed
2860
            ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
mercury233's avatar
mercury233 committed
2861 2862
            room.watchers.push(client);
            ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
2863 2864 2865
            ref1 = room.watcher_buffers;
            for (n = 0, len3 = ref1.length; n < len3; n++) {
              buffer = ref1[n];
mercury233's avatar
mercury233 committed
2866 2867 2868 2869 2870
              client.write(buffer);
            }
          } else {
            ygopro.stoc_die(client, "${watch_denied}");
          }
nanahira's avatar
nanahira committed
2871
        } else if (room.hostinfo.no_watch && room.players.length >= (room.hostinfo.mode === 2 ? 4 : 2)) {
nanahira's avatar
fix  
nanahira committed
2872
          ygopro.stoc_die(client, "${watch_denied_room}");
mercury233's avatar
mercury233 committed
2873
        } else {
nanahira's avatar
nanahira committed
2874 2875
          //client.room = room
          client.setTimeout(300000); //连接后超时5分钟
2876 2877
          client.rid = _.indexOf(ROOM_all, room);
          room.connect(client);
mercury233's avatar
mercury233 committed
2878
        }
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2879
      };
nanahira's avatar
nanahira committed
2880 2881
      _async.auto({
        match_permit: function(done) {
nanahira's avatar
fix  
nanahira committed
2882 2883 2884 2885
          if (client.closed) {
            done();
            return;
          }
nanahira's avatar
nanahira committed
2886 2887 2888
          if (!settings.modules.arena_mode.check_permit) {
            done(null, null);
            return;
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2889
          }
nanahira's avatar
nanahira committed
2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903
          request({
            url: settings.modules.arena_mode.check_permit,
            json: true,
            qs: {
              username: client.name,
              password: info.pass,
              arena: settings.modules.arena_mode.mode
            }
          }, function(error, response, body) {
            if (client.closed) {
              done(null, null);
              return;
            }
            if (!error && body) {
nanahira's avatar
fix  
nanahira committed
2904
              done(null, body);
nanahira's avatar
nanahira committed
2905 2906
            } else {
              log.warn("Match permit request error", error);
nanahira's avatar
fix  
nanahira committed
2907
              done(null, null);
nanahira's avatar
nanahira committed
2908 2909 2910 2911
            }
          });
        },
        get_user: function(done) {
nanahira's avatar
nanahira committed
2912
          var decrypted_buffer, i, id, len2, m, ref, secret;
nanahira's avatar
nanahira committed
2913
          if (client.closed) {
nanahira's avatar
fix  
nanahira committed
2914
            done();
nanahira's avatar
nanahira committed
2915
            return;
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2916
          }
nanahira's avatar
nanahira committed
2917 2918
          if (id = users_cache[client.name]) {
            secret = id % 65535 + 1;
nanahira's avatar
nanahira committed
2919
            decrypted_buffer = Buffer.allocUnsafe(6);
nanahira's avatar
nanahira committed
2920 2921 2922
            ref = [0, 2, 4];
            for (m = 0, len2 = ref.length; m < len2; m++) {
              i = ref[m];
nanahira's avatar
nanahira committed
2923 2924 2925
              decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i);
            }
            if (check_buffer_indentity(decrypted_buffer)) {
nanahira's avatar
nanahira committed
2926 2927 2928 2929
              done(null, {
                original: decrypted_buffer,
                decrypted: decrypted_buffer
              });
nanahira's avatar
fix  
nanahira committed
2930
              return;
nanahira's avatar
nanahira committed
2931 2932
            }
          }
nanahira's avatar
nanahira committed
2933
          //TODO: query database directly, like preload.
nanahira's avatar
nanahira committed
2934 2935 2936 2937 2938 2939 2940 2941 2942 2943
          request({
            baseUrl: settings.modules.mycard.auth_base_url,
            url: '/users/' + encodeURIComponent(client.name) + '.json',
            qs: {
              api_key: settings.modules.mycard.auth_key,
              api_username: client.name,
              skip_track_visit: true
            },
            json: true
          }, function(error, response, body) {
nanahira's avatar
nanahira committed
2944
            var len3, n, ref1;
nanahira's avatar
nanahira committed
2945 2946 2947 2948
            if (!error && body && body.user) {
              users_cache[client.name] = body.user.id;
              secret = body.user.id % 65535 + 1;
              decrypted_buffer = Buffer.allocUnsafe(6);
nanahira's avatar
nanahira committed
2949 2950 2951
              ref1 = [0, 2, 4];
              for (n = 0, len3 = ref1.length; n < len3; n++) {
                i = ref1[n];
nanahira's avatar
nanahira committed
2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965
                decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i);
              }
              if (check_buffer_indentity(decrypted_buffer)) {
                buffer = decrypted_buffer;
              }
            } else {
              log.warn("READ USER FAIL", error, body);
              done("${create_room_failed}");
              return;
            }
            if (!check_buffer_indentity(buffer)) {
              done('${invalid_password_checksum}');
              return;
            }
nanahira's avatar
fix  
nanahira committed
2966
            done(null, {
nanahira's avatar
nanahira committed
2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979
              original: buffer,
              decrypted: decrypted_buffer
            });
          });
        }
      }, function(err, data) {
        if (client.closed) {
          return;
        }
        if (err) {
          ygopro.stoc_die(client, err);
          return;
        }
nanahira's avatar
fix  
nanahira committed
2980
        return create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit);
nanahira's avatar
nanahira committed
2981
      });
nanahira's avatar
nanahira committed
2982
    } else if (settings.modules.challonge.enabled) {
2983 2984 2985 2986 2987 2988 2989
      if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
        info.version = settings.version;
        struct = ygopro.structs.get("CTOS_JoinGame");
        struct._setBuff(buffer);
        struct.set("version", info.version);
        buffer = struct.buffer;
      }
nanahira's avatar
nanahira committed
2990
      pre_room = ROOM_find_by_name(info.pass);
nanahira's avatar
nanahira committed
2991
      if (pre_room && pre_room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.cloud_replay.enable_halfway_watch && !pre_room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
2992
        room = pre_room;
nanahira's avatar
nanahira committed
2993
        client.setTimeout(300000); //连接后超时5分钟
nanahira's avatar
nanahira committed
2994 2995
        client.rid = _.indexOf(ROOM_all, room);
        client.is_post_watcher = true;
nanahira's avatar
nanahira committed
2996
        ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
nanahira's avatar
nanahira committed
2997 2998
        room.watchers.push(client);
        ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
2999 3000 3001
        ref = room.watcher_buffers;
        for (m = 0, len2 = ref.length; m < len2; m++) {
          buffer = ref[m];
nanahira's avatar
nanahira committed
3002 3003 3004 3005
          client.write(buffer);
        }
      } else {
        ygopro.stoc_send_chat(client, '${loading_user_info}', ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3006
        client.setTimeout(300000); //连接后超时5分钟
nanahira's avatar
nanahira committed
3007
        recover_match = info.pass.match(/^(RC|RECOVER)(\d*)T(\d*)$/);
nanahira's avatar
nanahira committed
3008 3009
        _async.auto({
          participant_data: function(done) {
nanahira's avatar
nanahira committed
3010 3011 3012 3013
            challonge.participants._index({
              id: settings.modules.challonge.tournament_id,
              callback: done
            });
nanahira's avatar
nanahira committed
3014 3015
          },
          match_data: function(done) {
nanahira's avatar
nanahira committed
3016 3017
            challonge.matches._index({
              id: settings.modules.challonge.tournament_id,
nanahira's avatar
nanahira committed
3018 3019
              callback: done,
              no_cache: !!recover_match
nanahira's avatar
nanahira committed
3020 3021
            });
          }
nanahira's avatar
nanahira committed
3022
        }, async function(err, datas) {
nanahira's avatar
nanahira committed
3023
          var create_room_name, found, k, len3, len4, match, n, o, player, ref1, ref2, ref3, ref4, user;
nanahira's avatar
nanahira committed
3024 3025 3026
          if (client.closed) {
            return;
          }
nanahira's avatar
nanahira committed
3027
          if (err || !datas.participant_data || !datas.match_data) {
nanahira's avatar
nanahira committed
3028 3029 3030 3031 3032
            log.warn("Failed loading Challonge user info", err);
            ygopro.stoc_die(client, '${challonge_match_load_failed}');
            return;
          }
          found = false;
nanahira's avatar
nanahira committed
3033 3034 3035
          ref1 = datas.participant_data;
          for (k in ref1) {
            user = ref1[k];
nanahira's avatar
nanahira committed
3036 3037 3038
            if (user.participant && user.participant.name && deck_name_match(user.participant.name, client.name)) {
              found = user.participant;
              break;
nanahira's avatar
nanahira committed
3039
            }
nanahira's avatar
nanahira committed
3040 3041 3042 3043 3044 3045 3046
          }
          if (!found) {
            ygopro.stoc_die(client, '${challonge_user_not_found}');
            return;
          }
          client.challonge_info = found;
          found = false;
nanahira's avatar
nanahira committed
3047 3048 3049
          ref2 = datas.match_data;
          for (k in ref2) {
            match = ref2[k];
nanahira's avatar
nanahira committed
3050 3051 3052
            if (match && match.match && !match.match.winnerId && match.match.state !== "complete" && match.match.player1Id && match.match.player2Id && (match.match.player1Id === client.challonge_info.id || match.match.player2Id === client.challonge_info.id)) {
              found = match.match;
              break;
nanahira's avatar
nanahira committed
3053
            }
nanahira's avatar
nanahira committed
3054 3055 3056 3057 3058
          }
          if (!found) {
            ygopro.stoc_die(client, '${challonge_match_not_found}');
            return;
          }
nanahira's avatar
nanahira committed
3059 3060 3061
          //if found.winnerId
          //  ygopro.stoc_die(client, '${challonge_match_already_finished}')
          //  return
nanahira's avatar
nanahira committed
3062 3063 3064 3065
          create_room_name = 'M#' + found.id;
          if (recover_match) {
            create_room_name = recover_match[0] + ',' + create_room_name;
          }
nanahira's avatar
nanahira committed
3066
          room = (await ROOM_find_or_create_by_name(create_room_name));
nanahira's avatar
nanahira committed
3067 3068
          if (room) {
            room.challonge_info = found;
nanahira's avatar
nanahira committed
3069
            // room.max_player = 2
nanahira's avatar
nanahira committed
3070 3071 3072 3073 3074 3075 3076 3077
            room.welcome = "${challonge_match_created}";
          }
          if (!room) {
            ygopro.stoc_die(client, "${server_full}");
          } else if (room.error) {
            ygopro.stoc_die(client, room.error);
          } else if (room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
            if (settings.modules.cloud_replay.enable_halfway_watch && !room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
3078
              //client.setTimeout(300000) #连接后超时5分钟
nanahira's avatar
nanahira committed
3079 3080
              client.rid = _.indexOf(ROOM_all, room);
              client.is_post_watcher = true;
nanahira's avatar
nanahira committed
3081
              ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
nanahira's avatar
nanahira committed
3082 3083
              room.watchers.push(client);
              ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3084 3085 3086
              ref3 = room.watcher_buffers;
              for (n = 0, len3 = ref3.length; n < len3; n++) {
                buffer = ref3[n];
nanahira's avatar
nanahira committed
3087
                client.write(buffer);
nanahira's avatar
nanahira committed
3088
              }
nanahira's avatar
nanahira committed
3089 3090
            } else {
              ygopro.stoc_die(client, "${watch_denied}");
nanahira's avatar
nanahira committed
3091
            }
nanahira's avatar
nanahira committed
3092 3093 3094
          } else if (room.hostinfo.no_watch && room.players.length >= (room.hostinfo.mode === 2 ? 4 : 2)) {
            ygopro.stoc_die(client, "${watch_denied_room}");
          } else {
nanahira's avatar
nanahira committed
3095 3096 3097
            ref4 = room.get_playing_player();
            for (o = 0, len4 = ref4.length; o < len4; o++) {
              player = ref4[o];
nanahira's avatar
nanahira committed
3098 3099 3100 3101
              if (!(player && player !== client && player.challonge_info.id === client.challonge_info.id)) {
                continue;
              }
              ygopro.stoc_die(client, "${challonge_player_already_in}");
nanahira's avatar
nanahira committed
3102 3103
              return;
            }
nanahira's avatar
nanahira committed
3104 3105
            //client.room = room
            //client.setTimeout(300000) #连接后超时5分钟
nanahira's avatar
nanahira committed
3106 3107
            client.rid = _.indexOf(ROOM_all, room);
            room.connect(client);
nanahira's avatar
nanahira committed
3108 3109 3110
          }
        });
      }
mercury233's avatar
mercury233 committed
3111
    } else if (!client.name || client.name === "") {
mercury233's avatar
mercury233 committed
3112
      ygopro.stoc_die(client, "${bad_user_name}");
mercury233's avatar
mercury233 committed
3113 3114
    } else if (ROOM_connected_ip[client.ip] > 5) {
      log.warn("MULTI LOGIN", client.name, client.ip);
mercury233's avatar
mercury233 committed
3115
      ygopro.stoc_die(client, "${too_much_connection}" + client.ip);
nanahira's avatar
nanahira committed
3116 3117 3118 3119 3120 3121
    } else if (settings.modules.mysql.enabled && (await dataManager.checkBan("name", client.name))) { //账号被封
      exactBan = (await dataManager.checkBanWithNameAndIP(client.name, client.ip));
      if (!exactBan) {
        exactBan = dataManager.getBan(client.name, client.ip);
        await dataManager.banPlayer(exactBan);
      }
mercury233's avatar
mercury233 committed
3122
      log.warn("BANNED USER LOGIN", client.name, client.ip);
mercury233's avatar
mercury233 committed
3123
      ygopro.stoc_die(client, "${banned_user_login}");
nanahira's avatar
nanahira committed
3124
    } else if (settings.modules.mysql.enabled && (await dataManager.checkBan("ip", client.ip))) { //IP被封
mercury233's avatar
mercury233 committed
3125
      log.warn("BANNED IP LOGIN", client.name, client.ip);
mercury233's avatar
mercury233 committed
3126
      ygopro.stoc_die(client, "${banned_ip_login}");
3127
    } else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && _.any(badwords.level3, function(badword) {
mercury233's avatar
mercury233 committed
3128
      var regexp;
mercury233's avatar
fix  
mercury233 committed
3129
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
3130 3131
      return name.match(regexp);
    }, name = client.name)) {
mercury233's avatar
mercury233 committed
3132
      log.warn("BAD NAME LEVEL 3", client.name, client.ip);
mercury233's avatar
mercury233 committed
3133
      ygopro.stoc_die(client, "${bad_name_level3}");
3134
    } else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && _.any(badwords.level2, function(badword) {
mercury233's avatar
mercury233 committed
3135
      var regexp;
mercury233's avatar
fix  
mercury233 committed
3136
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
3137 3138
      return name.match(regexp);
    }, name = client.name)) {
mercury233's avatar
mercury233 committed
3139
      log.warn("BAD NAME LEVEL 2", client.name, client.ip);
mercury233's avatar
mercury233 committed
3140
      ygopro.stoc_die(client, "${bad_name_level2}");
3141
    } else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && _.any(badwords.level1, function(badword) {
mercury233's avatar
mercury233 committed
3142
      var regexp;
mercury233's avatar
fix  
mercury233 committed
3143
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
3144 3145
      return name.match(regexp);
    }, name = client.name)) {
mercury233's avatar
mercury233 committed
3146
      log.warn("BAD NAME LEVEL 1", client.name, client.ip);
mercury233's avatar
mercury233 committed
3147
      ygopro.stoc_die(client, "${bad_name_level1}");
mercury233's avatar
mercury233 committed
3148
    } else if (info.pass.length && !ROOM_validate(info.pass)) {
mercury233's avatar
mercury233 committed
3149
      ygopro.stoc_die(client, "${invalid_password_room}");
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
3150
    } else {
3151 3152 3153 3154 3155 3156 3157
      if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
        info.version = settings.version;
        struct = ygopro.structs.get("CTOS_JoinGame");
        struct._setBuff(buffer);
        struct.set("version", info.version);
        buffer = struct.buffer;
      }
nanahira's avatar
nanahira committed
3158
      //log.info 'join_game',info.pass, client.name
nanahira's avatar
nanahira committed
3159
      room = (await ROOM_find_or_create_by_name(info.pass, client.ip));
mercury233's avatar
mercury233 committed
3160
      if (!room) {
mercury233's avatar
mercury233 committed
3161
        ygopro.stoc_die(client, "${server_full}");
mercury233's avatar
mercury233 committed
3162
      } else if (room.error) {
mercury233's avatar
mercury233 committed
3163
        ygopro.stoc_die(client, room.error);
nanahira's avatar
nanahira committed
3164
      } else if (room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
nanahira's avatar
nanahira committed
3165
        if (settings.modules.cloud_replay.enable_halfway_watch && !room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
3166
          client.setTimeout(300000); //连接后超时5分钟
3167
          client.rid = _.indexOf(ROOM_all, room);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3168
          client.is_post_watcher = true;
nanahira's avatar
nanahira committed
3169
          ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
3170
          room.watchers.push(client);
mercury233's avatar
mercury233 committed
3171
          ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3172 3173 3174
          ref1 = room.watcher_buffers;
          for (n = 0, len3 = ref1.length; n < len3; n++) {
            buffer = ref1[n];
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3175 3176 3177
            client.write(buffer);
          }
        } else {
mercury233's avatar
mercury233 committed
3178
          ygopro.stoc_die(client, "${watch_denied}");
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3179
        }
nanahira's avatar
nanahira committed
3180
      } else if (room.hostinfo.no_watch && room.players.length >= (room.hostinfo.mode === 2 ? 4 : 2)) {
nanahira's avatar
nanahira committed
3181
        ygopro.stoc_die(client, "${watch_denied_room}");
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3182
      } else {
nanahira's avatar
nanahira committed
3183
        client.setTimeout(300000); //连接后超时5分钟
3184 3185
        client.rid = _.indexOf(ROOM_all, room);
        room.connect(client);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3186
      }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
3187 3188 3189
    }
  });

nanahira's avatar
nanahira committed
3190
  ygopro.stoc_follow('JOIN_GAME', false, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3191
    var j, len, player, recorder, ref, room, watcher;
nanahira's avatar
nanahira committed
3192
    //欢迎信息
3193
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3194
    if (!(room && !client.reconnecting)) {
3195 3196
      return;
    }
3197 3198 3199
    if (!room.join_game_buffer) {
      room.join_game_buffer = buffer;
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3200
    if (settings.modules.welcome) {
mercury233's avatar
mercury233 committed
3201
      ygopro.stoc_send_chat(client, settings.modules.welcome, ygopro.constants.COLORS.GREEN);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3202
    }
3203 3204
    if (room.welcome) {
      ygopro.stoc_send_chat(client, room.welcome, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
add  
mercury233 committed
3205
    }
3206 3207 3208
    if (room.welcome2) {
      ygopro.stoc_send_chat(client, room.welcome2, ygopro.constants.COLORS.PINK);
    }
nanahira's avatar
nanahira committed
3209
    if (settings.modules.arena_mode.enabled && !client.is_local) { //and not client.score_shown
mercury233's avatar
mercury233 committed
3210 3211 3212 3213
      request({
        url: settings.modules.arena_mode.get_score + encodeURIComponent(client.name),
        json: true
      }, function(error, response, body) {
mercury233's avatar
merge  
mercury233 committed
3214
        var rank_txt;
mercury233's avatar
merge  
mercury233 committed
3215 3216 3217 3218
        if (error) {
          log.warn('LOAD SCORE ERROR', client.name, error);
        } else if (!body || _.isString(body)) {
          log.warn('LOAD SCORE FAIL', client.name, response.statusCode, response.statusMessage, body);
mercury233's avatar
mercury233 committed
3219
        } else {
nanahira's avatar
nanahira committed
3220
          //log.info 'LOAD SCORE', client.name, body
mercury233's avatar
mercury233 committed
3221
          rank_txt = body.arena_rank > 0 ? "${rank_arena}" + body.arena_rank : "${rank_blank}";
nanahira's avatar
nanahira committed
3222
          ygopro.stoc_send_chat(client, `${client.name}\${exp_value_part1}${body.exp}\${exp_value_part2}\${exp_value_part3}${Math.round(body.pt)}${rank_txt}\${exp_value_part4}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
3223 3224 3225
        }
      });
    }
nanahira's avatar
nanahira committed
3226
    //client.score_shown = true
3227 3228
    if (settings.modules.random_duel.record_match_scores && room.random_type === 'M') {
      ygopro.stoc_send_chat_to_room(room, ROOM_player_get_score(client), ygopro.constants.COLORS.GREEN);
nanahira's avatar
nanahira committed
3229 3230 3231
      ref = room.players;
      for (j = 0, len = ref.length; j < len; j++) {
        player = ref[j];
3232 3233 3234 3235 3236
        if (player.pos !== 7 && player !== client) {
          ygopro.stoc_send_chat(client, ROOM_player_get_score(player), ygopro.constants.COLORS.GREEN);
        }
      }
    }
3237 3238
    if (!room.recorder) {
      room.recorder = recorder = net.connect(room.port, function() {
mercury233's avatar
mercury233 committed
3239 3240 3241 3242 3243
        ygopro.ctos_send(recorder, 'PLAYER_INFO', {
          name: "Marshtomp"
        });
        ygopro.ctos_send(recorder, 'JOIN_GAME', {
          version: settings.version,
mercury233's avatar
mercury233 committed
3244
          pass: "Marshtomp"
mercury233's avatar
mercury233 committed
3245
        });
mercury233's avatar
mercury233 committed
3246
        ygopro.ctos_send(recorder, 'HS_TOOBSERVER');
mercury233's avatar
mercury233 committed
3247 3248
      });
      recorder.on('data', function(data) {
3249
        room = ROOM_all[client.rid];
mercury233's avatar
mercury233 committed
3250
        if (!(room && settings.modules.cloud_replay.enabled)) {
mercury233's avatar
mercury233 committed
3251 3252
          return;
        }
mercury233's avatar
mercury233 committed
3253
        room.recorder_buffers.push(data);
mercury233's avatar
mercury233 committed
3254 3255 3256
      });
      recorder.on('error', function(error) {});
    }
nanahira's avatar
nanahira committed
3257
    if (settings.modules.cloud_replay.enable_halfway_watch && !room.watcher && !room.hostinfo.no_watch) {
3258
      room.watcher = watcher = settings.modules.test_mode.watch_public_hand ? room.recorder : net.connect(room.port, function() {
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3259 3260 3261 3262 3263
        ygopro.ctos_send(watcher, 'PLAYER_INFO', {
          name: "the Big Brother"
        });
        ygopro.ctos_send(watcher, 'JOIN_GAME', {
          version: settings.version,
mercury233's avatar
mercury233 committed
3264
          pass: "the Big Brother"
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3265
        });
mercury233's avatar
mercury233 committed
3266
        ygopro.ctos_send(watcher, 'HS_TOOBSERVER');
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3267
      });
mercury233's avatar
fix  
mercury233 committed
3268
      watcher.on('data', function(data) {
nanahira's avatar
nanahira committed
3269
        var l, len1, ref1, w;
3270 3271
        room = ROOM_all[client.rid];
        if (!room) {
3272 3273
          return;
        }
3274
        room.watcher_buffers.push(data);
nanahira's avatar
nanahira committed
3275 3276 3277
        ref1 = room.watchers;
        for (l = 0, len1 = ref1.length; l < len1; l++) {
          w = ref1[l];
nanahira's avatar
nanahira committed
3278
          if (w) { //a WTF fix
mercury233's avatar
test3  
mercury233 committed
3279
            w.write(data);
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3280
          }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3281 3282
        }
      });
mercury233's avatar
mercury233 committed
3283 3284 3285
      watcher.on('error', function(error) {
        log.error("watcher error", error);
      });
神楽坂玲奈's avatar
神楽坂玲奈 committed
3286
    }
神楽坂玲奈's avatar
tip  
神楽坂玲奈 committed
3287
  });
神楽坂玲奈's avatar
神楽坂玲奈 committed
3288

nanahira's avatar
nanahira committed
3289
  // 登场台词
nanahira's avatar
nanahira committed
3290 3291
  load_dialogues = global.load_dialogues = async function() {
    return (await loadRemoteData(dialogues, "dialogues", settings.modules.dialogues.get));
mercury233's avatar
mercury233 committed
3292 3293
  };

nanahira's avatar
nanahira committed
3294
  ygopro.stoc_follow('GAME_MSG', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3295
    var card, chain, check, count, cpos, deck_found, found, hint_type, i, id, j, l, len, len1, len2, len3, limbo_found, line, loc, m, max_loop, msg, n, o, oppo_pos, phase, player, playertype, pos, ppos, reason, ref, ref1, ref2, ref3, ref4, ref5, room, trigger_location, val, win_pos;
3296
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3297
    if (!(room && !client.reconnecting)) {
3298 3299
      return;
    }
神楽坂玲奈's avatar
神楽坂玲奈 committed
3300
    msg = buffer.readInt8(0);
nanahira's avatar
nanahira committed
3301 3302 3303 3304 3305
    //console.log client.pos, "MSG", ygopro.constants.MSG[msg]
    if (ygopro.constants.MSG[msg] === 'RETRY' && room.recovering) {
      room.finish_recover(true);
      return true;
    }
nanahira's avatar
nanahira committed
3306 3307 3308 3309 3310 3311 3312 3313 3314 3315
    if (settings.modules.retry_handle.enabled) {
      if (ygopro.constants.MSG[msg] === 'RETRY') {
        if (client.retry_count == null) {
          client.retry_count = 0;
        }
        client.retry_count++;
        log.warn("MSG_RETRY detected", client.name, client.ip, msg, client.retry_count);
        if (settings.modules.retry_handle.max_retry_count && client.retry_count >= settings.modules.retry_handle.max_retry_count) {
          ygopro.stoc_send_chat_to_room(room, client.name + "${retry_too_much_room_part1}" + settings.modules.retry_handle.max_retry_count + "${retry_too_much_room_part2}", ygopro.constants.COLORS.BABYBLUE);
          ygopro.stoc_send_chat(client, "${retry_too_much_part1}" + settings.modules.retry_handle.max_retry_count + "${retry_too_much_part2}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3316
          CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
3317
          CLIENT_kick(client);
nanahira's avatar
nanahira committed
3318 3319 3320 3321 3322 3323 3324 3325
          return true;
        }
        if (client.last_game_msg) {
          if (settings.modules.retry_handle.max_retry_count) {
            ygopro.stoc_send_chat(client, "${retry_part1}" + client.retry_count + "${retry_part2}" + settings.modules.retry_handle.max_retry_count + "${retry_part3}", ygopro.constants.COLORS.RED);
          } else {
            ygopro.stoc_send_chat(client, "${retry_not_counted}", ygopro.constants.COLORS.BABYBLUE);
          }
nanahira's avatar
nanahira committed
3326 3327 3328
          if (client.last_hint_msg) {
            ygopro.stoc_send(client, 'GAME_MSG', client.last_hint_msg);
          }
nanahira's avatar
nanahira committed
3329 3330 3331 3332 3333
          ygopro.stoc_send(client, 'GAME_MSG', client.last_game_msg);
          return true;
        }
      } else {
        client.last_game_msg = buffer;
nanahira's avatar
nanahira committed
3334
        client.last_game_msg_title = ygopro.constants.MSG[msg];
nanahira's avatar
nanahira committed
3335
      }
nanahira's avatar
nanahira committed
3336
    // log.info(client.name, client.last_game_msg_title)
nanahira's avatar
nanahira committed
3337 3338 3339
    } else if (ygopro.constants.MSG[msg] !== 'RETRY') {
      client.last_game_msg = buffer;
      client.last_game_msg_title = ygopro.constants.MSG[msg];
nanahira's avatar
nanahira committed
3340
    }
nanahira's avatar
nanahira committed
3341 3342
    // log.info(client.name, client.last_game_msg_title)
    if ((msg >= 10 && msg < 30) || msg === 132 || (msg >= 140 && msg <= 144)) { //SELECT和ANNOUNCE开头的消息
nanahira's avatar
nanahira committed
3343 3344 3345 3346 3347 3348 3349 3350 3351 3352
      if (room.recovering) {
        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();
      }
3353
    }
nanahira's avatar
nanahira committed
3354 3355 3356
    //log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}")

    //log.info 'MSG', ygopro.constants.MSG[msg]
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3357 3358 3359
    if (ygopro.constants.MSG[msg] === 'START') {
      playertype = buffer.readUInt8(1);
      client.is_first = !(playertype & 0xf);
3360
      client.lp = room.hostinfo.start_lp;
nanahira's avatar
nanahira committed
3361 3362 3363
      if (room.hostinfo.mode !== 2) {
        client.card_count = 0;
      }
nanahira's avatar
nanahira committed
3364
      room.duel_stage = ygopro.constants.DUEL_STAGE.DUELING;
nanahira's avatar
nanahira committed
3365
      if (client.pos === 0) {
3366
        room.turn = 0;
nanahira's avatar
nanahira committed
3367
        room.duel_count++;
nanahira's avatar
nanahira committed
3368 3369 3370 3371 3372 3373 3374
        if (room.death && room.duel_count > 1) {
          if (room.death === -1) {
            ygopro.stoc_send_chat_to_room(room, "${death_start_final}", ygopro.constants.COLORS.BABYBLUE);
          } else {
            ygopro.stoc_send_chat_to_room(room, "${death_start_extra}", ygopro.constants.COLORS.BABYBLUE);
          }
        }
nanahira's avatar
nanahira committed
3375 3376 3377
        if (room.recovering) {
          ygopro.stoc_send_chat_to_room(room, "${recover_start_hint}", ygopro.constants.COLORS.BABYBLUE);
        }
nanahira's avatar
fix  
nanahira committed
3378
      }
nanahira's avatar
nanahira committed
3379
      if (client.is_first && (room.hostinfo.mode !== 2 || client.pos === 0 || client.pos === 2)) {
nanahira's avatar
nanahira committed
3380
        room.first_list.push(client.name_vpass);
nanahira's avatar
nanahira committed
3381
      }
nanahira's avatar
nanahira committed
3382 3383 3384 3385
      if (settings.modules.retry_handle.enabled) {
        client.retry_count = 0;
        client.last_game_msg = null;
      }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3386
    }
nanahira's avatar
nanahira committed
3387
    //ygopro.stoc_send_chat_to_room(room, "LP跟踪调试信息: #{client.name} 初始LP #{client.lp}")
nanahira's avatar
nanahira committed
3388 3389
    if (ygopro.constants.MSG[msg] === 'HINT') {
      hint_type = buffer.readUInt8(1);
nanahira's avatar
typo  
nanahira committed
3390
      if (hint_type === 3) {
nanahira's avatar
nanahira committed
3391 3392 3393
        client.last_hint_msg = buffer;
      }
    }
3394
    if (ygopro.constants.MSG[msg] === 'NEW_TURN') {
nanahira's avatar
nanahira committed
3395
      if (client.pos === 0) {
nanahira's avatar
nanahira committed
3396
        room.turn++;
nanahira's avatar
nanahira committed
3397 3398 3399
        if (room.recovering && room.recover_from_turn <= room.turn) {
          room.finish_recover();
        }
nanahira's avatar
nanahira committed
3400
        if (room.death && room.death !== -2) {
nanahira's avatar
nanahira committed
3401
          if (room.turn >= room.death) {
nanahira's avatar
nanahira committed
3402 3403 3404
            oppo_pos = room.hostinfo.mode === 2 ? 2 : 1;
            if (room.dueling_players[0].lp !== room.dueling_players[oppo_pos].lp && room.turn > 1) {
              win_pos = room.dueling_players[0].lp > room.dueling_players[oppo_pos].lp ? 0 : oppo_pos;
nanahira's avatar
nanahira committed
3405
              ygopro.stoc_send_chat_to_room(room, "${death_finish_part1}" + room.dueling_players[win_pos].name + "${death_finish_part2}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3406
              if (room.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
3407
                room.finished_by_death = true;
nanahira's avatar
nanahira committed
3408 3409
                ygopro.stoc_send(room.dueling_players[oppo_pos - win_pos], 'DUEL_END');
                ygopro.stoc_send(room.dueling_players[oppo_pos - win_pos + 1], 'DUEL_END');
3410
                room.scores[room.dueling_players[oppo_pos - win_pos].name_vpass] = -1;
nanahira's avatar
nanahira committed
3411 3412
                CLIENT_kick(room.dueling_players[oppo_pos - win_pos]);
                CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]);
nanahira's avatar
nanahira committed
3413 3414 3415
              } else {
                ygopro.ctos_send(room.dueling_players[oppo_pos - win_pos].server, 'SURRENDER');
              }
nanahira's avatar
nanahira committed
3416
            } else {
nanahira's avatar
nanahira committed
3417
              room.death = -1;
nanahira's avatar
nanahira committed
3418 3419 3420 3421 3422 3423
              ygopro.stoc_send_chat_to_room(room, "${death_remain_final}", ygopro.constants.COLORS.BABYBLUE);
            }
          } else {
            ygopro.stoc_send_chat_to_room(room, "${death_remain_part1}" + (room.death - room.turn) + "${death_remain_part2}", ygopro.constants.COLORS.BABYBLUE);
          }
        }
3424 3425 3426 3427 3428
      }
      if (client.surrend_confirm) {
        client.surrend_confirm = false;
        ygopro.stoc_send_chat(client, "${surrender_canceled}", ygopro.constants.COLORS.BABYBLUE);
      }
mercury233's avatar
mercury233 committed
3429
    }
nanahira's avatar
nanahira committed
3430 3431 3432
    if (ygopro.constants.MSG[msg] === 'NEW_PHASE') {
      phase = buffer.readInt16LE(1);
      oppo_pos = room.hostinfo.mode === 2 ? 2 : 1;
nanahira's avatar
fix  
nanahira committed
3433 3434 3435 3436 3437
      if (client.pos === 0 && room.death === -2 && !(phase === 0x1 && room.turn < 2)) {
        if (room.dueling_players[0].lp !== room.dueling_players[oppo_pos].lp) {
          win_pos = room.dueling_players[0].lp > room.dueling_players[oppo_pos].lp ? 0 : oppo_pos;
          ygopro.stoc_send_chat_to_room(room, "${death_finish_part1}" + room.dueling_players[win_pos].name + "${death_finish_part2}", ygopro.constants.COLORS.BABYBLUE);
          if (room.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
3438
            room.finished_by_death = true;
nanahira's avatar
fix  
nanahira committed
3439 3440
            ygopro.stoc_send(room.dueling_players[oppo_pos - win_pos], 'DUEL_END');
            ygopro.stoc_send(room.dueling_players[oppo_pos - win_pos + 1], 'DUEL_END');
3441
            room.scores[room.dueling_players[oppo_pos - win_pos].name_vpass] = -1;
nanahira's avatar
nanahira committed
3442 3443
            CLIENT_kick(room.dueling_players[oppo_pos - win_pos]);
            CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]);
nanahira's avatar
fix  
nanahira committed
3444 3445 3446
          } else {
            ygopro.ctos_send(room.dueling_players[oppo_pos - win_pos].server, 'SURRENDER');
          }
nanahira's avatar
nanahira committed
3447
        } else {
nanahira's avatar
fix  
nanahira committed
3448 3449
          room.death = -1;
          ygopro.stoc_send_chat_to_room(room, "${death_remain_final}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3450 3451 3452
        }
      }
    }
nanahira's avatar
nanahira committed
3453
    if (ygopro.constants.MSG[msg] === 'WIN' && client.pos === 0) {
nanahira's avatar
nanahira committed
3454 3455 3456 3457
      if (room.recovering) {
        room.finish_recover(true);
        return true;
      }
mercury233's avatar
mercury233 committed
3458
      pos = buffer.readUInt8(1);
nanahira's avatar
nanahira committed
3459
      if (!(client.is_first || pos === 2 || room.duel_stage !== ygopro.constants.DUEL_STAGE.DUELING)) {
mercury233's avatar
mercury233 committed
3460 3461
        pos = 1 - pos;
      }
nanahira's avatar
nanahira committed
3462 3463 3464
      if (pos >= 0 && room.hostinfo.mode === 2) {
        pos = pos * 2;
      }
mercury233's avatar
mercury233 committed
3465
      reason = buffer.readUInt8(2);
nanahira's avatar
nanahira committed
3466 3467
      //log.info {winner: pos, reason: reason}
      //room.duels.push {winner: pos, reason: reason}
mercury233's avatar
mercury233 committed
3468
      room.winner = pos;
nanahira's avatar
nanahira committed
3469
      room.turn = 0;
nanahira's avatar
nanahira committed
3470
      room.duel_stage = ygopro.constants.DUEL_STAGE.END;
nanahira's avatar
nanahira committed
3471
      if (settings.modules.heartbeat_detection.enabled) {
nanahira's avatar
nanahira committed
3472 3473 3474
        ref = room.players;
        for (j = 0, len = ref.length; j < len; j++) {
          player = ref[j];
3475
          player.heartbeat_protected = false;
nanahira's avatar
nanahira committed
3476
        }
nanahira's avatar
nanahira committed
3477 3478
        delete room.long_resolve_card;
        delete room.long_resolve_chain;
nanahira's avatar
nanahira committed
3479
      }
mercury233's avatar
fix  
mercury233 committed
3480
      if (room && !room.finished && room.dueling_players[pos]) {
3481
        room.winner_name = room.dueling_players[pos].name_vpass;
nanahira's avatar
nanahira committed
3482
        //log.info room.dueling_players, pos
mercury233's avatar
mercury233 committed
3483
        room.scores[room.winner_name] = room.scores[room.winner_name] + 1;
nanahira's avatar
nanahira committed
3484 3485 3486 3487
        if (room.match_kill) {
          room.match_kill = false;
          room.scores[room.winner_name] = 99;
        }
mercury233's avatar
mercury233 committed
3488
      }
nanahira's avatar
fix  
nanahira committed
3489
      if (room.death) {
nanahira's avatar
nanahira committed
3490
        if (settings.modules.http.quick_death_rule === 1 || settings.modules.http.quick_death_rule === 3) {
nanahira's avatar
fix  
nanahira committed
3491 3492 3493 3494 3495
          room.death = -1;
        } else {
          room.death = 5;
        }
      }
mercury233's avatar
mercury233 committed
3496
    }
nanahira's avatar
nanahira committed
3497 3498 3499
    if (ygopro.constants.MSG[msg] === 'MATCH_KILL' && client.pos === 0) {
      room.match_kill = true;
    }
nanahira's avatar
nanahira committed
3500
    //lp跟踪
nanahira's avatar
nanahira committed
3501
    if (ygopro.constants.MSG[msg] === 'DAMAGE' && client.pos === 0) {
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3502 3503 3504 3505
      pos = buffer.readUInt8(1);
      if (!client.is_first) {
        pos = 1 - pos;
      }
nanahira's avatar
nanahira committed
3506 3507 3508
      if (pos >= 0 && room.hostinfo.mode === 2) {
        pos = pos * 2;
      }
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3509
      val = buffer.readInt32LE(2);
3510
      room.dueling_players[pos].lp -= val;
nanahira's avatar
fix  
nanahira committed
3511 3512 3513
      if (room.dueling_players[pos].lp < 0) {
        room.dueling_players[pos].lp = 0;
      }
nanahira's avatar
nanahira committed
3514
      if ((0 < (ref1 = room.dueling_players[pos].lp) && ref1 <= 100)) {
mercury233's avatar
mercury233 committed
3515
        ygopro.stoc_send_chat_to_room(room, "${lp_low_opponent}", ygopro.constants.COLORS.PINK);
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3516 3517
      }
    }
nanahira's avatar
nanahira committed
3518
    if (ygopro.constants.MSG[msg] === 'RECOVER' && client.pos === 0) {
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3519 3520 3521 3522
      pos = buffer.readUInt8(1);
      if (!client.is_first) {
        pos = 1 - pos;
      }
nanahira's avatar
nanahira committed
3523 3524 3525
      if (pos >= 0 && room.hostinfo.mode === 2) {
        pos = pos * 2;
      }
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3526
      val = buffer.readInt32LE(2);
3527
      room.dueling_players[pos].lp += val;
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3528
    }
nanahira's avatar
nanahira committed
3529
    if (ygopro.constants.MSG[msg] === 'LPUPDATE' && client.pos === 0) {
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3530 3531 3532 3533
      pos = buffer.readUInt8(1);
      if (!client.is_first) {
        pos = 1 - pos;
      }
nanahira's avatar
nanahira committed
3534 3535 3536
      if (pos >= 0 && room.hostinfo.mode === 2) {
        pos = pos * 2;
      }
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3537
      val = buffer.readInt32LE(2);
3538
      room.dueling_players[pos].lp = val;
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3539
    }
nanahira's avatar
nanahira committed
3540
    if (ygopro.constants.MSG[msg] === 'PAY_LPCOST' && client.pos === 0) {
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3541 3542 3543 3544
      pos = buffer.readUInt8(1);
      if (!client.is_first) {
        pos = 1 - pos;
      }
nanahira's avatar
nanahira committed
3545 3546 3547
      if (pos >= 0 && room.hostinfo.mode === 2) {
        pos = pos * 2;
      }
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3548
      val = buffer.readInt32LE(2);
3549
      room.dueling_players[pos].lp -= val;
nanahira's avatar
fix  
nanahira committed
3550 3551 3552
      if (room.dueling_players[pos].lp < 0) {
        room.dueling_players[pos].lp = 0;
      }
nanahira's avatar
nanahira committed
3553
      if ((0 < (ref2 = room.dueling_players[pos].lp) && ref2 <= 100)) {
mercury233's avatar
mercury233 committed
3554
        ygopro.stoc_send_chat_to_room(room, "${lp_low_self}", ygopro.constants.COLORS.PINK);
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3555 3556
      }
    }
nanahira's avatar
nanahira committed
3557 3558
    //track card count
    //todo: track card count in tag mode
nanahira's avatar
nanahira committed
3559
    if (ygopro.constants.MSG[msg] === 'MOVE' && room.hostinfo.mode !== 2) {
nanahira's avatar
nanahira committed
3560 3561 3562 3563 3564
      pos = buffer.readUInt8(5);
      if (!client.is_first) {
        pos = 1 - pos;
      }
      loc = buffer.readUInt8(6);
nanahira's avatar
nanahira committed
3565 3566
      if ((loc & 0xe) && pos === 0) {
        client.card_count--;
nanahira's avatar
nanahira committed
3567 3568 3569 3570 3571 3572
      }
      pos = buffer.readUInt8(9);
      if (!client.is_first) {
        pos = 1 - pos;
      }
      loc = buffer.readUInt8(10);
nanahira's avatar
nanahira committed
3573 3574
      if ((loc & 0xe) && pos === 0) {
        client.card_count++;
nanahira's avatar
nanahira committed
3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586
      }
    }
    if (ygopro.constants.MSG[msg] === 'DRAW' && room.hostinfo.mode !== 2) {
      pos = buffer.readUInt8(1);
      if (!client.is_first) {
        pos = 1 - pos;
      }
      if (pos === 0) {
        count = buffer.readInt8(2);
        client.card_count += count;
      }
    }
nanahira's avatar
nanahira committed
3587
    // check panel confirming cards in heartbeat
nanahira's avatar
nanahira committed
3588 3589 3590 3591 3592
    if (settings.modules.heartbeat_detection.enabled && ygopro.constants.MSG[msg] === 'CONFIRM_CARDS') {
      check = false;
      count = buffer.readInt8(2);
      max_loop = 3 + (count - 1) * 7;
      deck_found = 0;
nanahira's avatar
nanahira committed
3593
      limbo_found = 0; // support custom cards which may be in location 0 in KoishiPro or EdoPro
nanahira's avatar
nanahira committed
3594
      for (i = l = 3, ref3 = max_loop; l <= ref3; i = l += 7) {
nanahira's avatar
nanahira committed
3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606
        loc = buffer.readInt8(i + 5);
        if ((loc & 0x41) > 0) {
          deck_found++;
        } else if (loc === 0) {
          limbo_found++;
        }
        if ((deck_found > 0 && count > 1) || limbo_found > 0) {
          check = true;
          break;
        }
      }
      if (check) {
nanahira's avatar
nanahira committed
3607
        //console.log("Confirming cards:" + client.name)
3608
        client.heartbeat_protected = true;
nanahira's avatar
nanahira committed
3609 3610
      }
    }
nanahira's avatar
nanahira committed
3611
    // chain detection
nanahira's avatar
nanahira committed
3612 3613 3614 3615
    if (settings.modules.heartbeat_detection.enabled && client.pos === 0) {
      if (ygopro.constants.MSG[msg] === 'CHAINING') {
        card = buffer.readUInt32LE(1);
        found = false;
nanahira's avatar
nanahira committed
3616 3617
        for (m = 0, len1 = long_resolve_cards.length; m < len1; m++) {
          id = long_resolve_cards[m];
nanahira's avatar
nanahira committed
3618 3619 3620 3621 3622 3623 3624 3625 3626
          if (!(id === card)) {
            continue;
          }
          found = true;
          break;
        }
        if (found) {
          room.long_resolve_card = card;
        } else {
nanahira's avatar
nanahira committed
3627
          // console.log(0,card)
nanahira's avatar
nanahira committed
3628 3629 3630 3631 3632 3633 3634 3635
          delete room.long_resolve_card;
        }
      } else if (ygopro.constants.MSG[msg] === 'CHAINED' && room.long_resolve_card) {
        chain = buffer.readInt8(1);
        if (!room.long_resolve_chain) {
          room.long_resolve_chain = [];
        }
        room.long_resolve_chain[chain] = true;
nanahira's avatar
nanahira committed
3636
        // console.log(1,chain)
nanahira's avatar
nanahira committed
3637 3638 3639
        delete room.long_resolve_card;
      } else if (ygopro.constants.MSG[msg] === 'CHAIN_SOLVING' && room.long_resolve_chain) {
        chain = buffer.readInt8(1);
nanahira's avatar
nanahira committed
3640
        // console.log(2,chain)
nanahira's avatar
nanahira committed
3641
        if (room.long_resolve_chain[chain]) {
nanahira's avatar
nanahira committed
3642 3643 3644
          ref4 = room.get_playing_player();
          for (n = 0, len2 = ref4.length; n < len2; n++) {
            player = ref4[n];
nanahira's avatar
nanahira committed
3645 3646 3647 3648 3649
            player.heartbeat_protected = true;
          }
        }
      } else if ((ygopro.constants.MSG[msg] === 'CHAIN_NEGATED' || ygopro.constants.MSG[msg] === 'CHAIN_DISABLED') && room.long_resolve_chain) {
        chain = buffer.readInt8(1);
nanahira's avatar
nanahira committed
3650
        // console.log(3,chain)
nanahira's avatar
nanahira committed
3651 3652
        delete room.long_resolve_chain[chain];
      } else if (ygopro.constants.MSG[msg] === 'CHAIN_END') {
nanahira's avatar
nanahira committed
3653
        // console.log(4,chain)
nanahira's avatar
nanahira committed
3654 3655 3656 3657
        delete room.long_resolve_card;
        delete room.long_resolve_chain;
      }
    }
nanahira's avatar
nanahira committed
3658
    //登场台词
nanahira's avatar
nanahira committed
3659
    if (settings.modules.dialogues.enabled && !room.recovering) {
nanahira's avatar
nanahira committed
3660
      if (ygopro.constants.MSG[msg] === 'SUMMONING' || ygopro.constants.MSG[msg] === 'SPSUMMONING' || ygopro.constants.MSG[msg] === 'CHAINING') {
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3661
        card = buffer.readUInt32LE(1);
nanahira's avatar
fix  
nanahira committed
3662
        trigger_location = buffer.readUInt8(6);
nanahira's avatar
nanahira committed
3663
        if (dialogues.dialogues[card] && (ygopro.constants.MSG[msg] !== 'CHAINING' || (trigger_location & 0x8) && client.ready_trap)) {
nanahira's avatar
nanahira committed
3664 3665 3666
          ref5 = _.lines(dialogues.dialogues[card][Math.floor(Math.random() * dialogues.dialogues[card].length)]);
          for (o = 0, len3 = ref5.length; o < len3; o++) {
            line = ref5[o];
mercury233's avatar
mercury233 committed
3667
            ygopro.stoc_send_chat(client, line, ygopro.constants.COLORS.PINK);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3668
          }
神楽坂玲奈's avatar
神楽坂玲奈 committed
3669
        }
神楽坂玲奈's avatar
 
神楽坂玲奈 committed
3670
      }
nanahira's avatar
nanahira committed
3671 3672 3673 3674 3675 3676 3677 3678
      if (ygopro.constants.MSG[msg] === 'POS_CHANGE') {
        loc = buffer.readUInt8(6);
        ppos = buffer.readUInt8(8);
        cpos = buffer.readUInt8(9);
        client.ready_trap = !!(loc & 0x8) && !!(ppos & 0xa) && !!(cpos & 0x5);
      } else if (ygopro.constants.MSG[msg] !== 'UPDATE_CARD' && ygopro.constants.MSG[msg] !== 'WAITING') {
        client.ready_trap = false;
      }
神楽坂玲奈's avatar
 
神楽坂玲奈 committed
3679
    }
nanahira's avatar
nanahira committed
3680 3681 3682 3683 3684 3685
    if (room.recovering && client.pos < 4) {
      if (ygopro.constants.MSG[msg] !== 'WAITING') {
        room.recover_buffers[client.pos].push(buffer);
      }
      return true;
    }
nanahira's avatar
nanahira committed
3686
    return false;
神楽坂玲奈's avatar
 
神楽坂玲奈 committed
3687 3688
  });

nanahira's avatar
nanahira committed
3689
  //房间管理
nanahira's avatar
nanahira committed
3690
  ygopro.ctos_follow('HS_TOOBSERVER', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3691
    var j, len, player, ref, room;
3692 3693 3694 3695
    room = ROOM_all[client.rid];
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
3696
    if (room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
3697 3698 3699
      ygopro.stoc_send_chat(client, "${watch_denied_room}", ygopro.constants.COLORS.RED);
      return true;
    }
nanahira's avatar
nanahira committed
3700
    if ((!room.arena && !settings.modules.challonge.enabled) || client.is_local) {
3701 3702
      return false;
    }
nanahira's avatar
nanahira committed
3703 3704 3705
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
3706 3707 3708 3709 3710 3711 3712
      if (player === client) {
        ygopro.stoc_send_chat(client, "${cannot_to_observer}", ygopro.constants.COLORS.BABYBLUE);
        return true;
      }
    }
    return false;
  });
nanahira's avatar
js  
nanahira committed
3713

nanahira's avatar
nanahira committed
3714
  ygopro.ctos_follow('HS_KICK', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3715
    var j, len, player, ref, room;
3716 3717
    room = ROOM_all[client.rid];
    if (!room) {
3718 3719
      return;
    }
nanahira's avatar
nanahira committed
3720 3721 3722
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
mercury233's avatar
mercury233 committed
3723
      if (player && player.pos === info.pos && player !== client) {
nanahira's avatar
nanahira committed
3724
        if (room.arena === "athletic" || settings.modules.challonge.enabled) {
nanahira's avatar
nanahira committed
3725
          ygopro.stoc_send_chat_to_room(room, `${client.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3726
          CLIENT_kick(client);
3727 3728
          return true;
        }
mercury233's avatar
mercury233 committed
3729
        client.kick_count = client.kick_count ? client.kick_count + 1 : 1;
mercury233's avatar
fix  
mercury233 committed
3730
        if (client.kick_count >= 5 && room.random_type) {
nanahira's avatar
nanahira committed
3731
          ygopro.stoc_send_chat_to_room(room, `${client.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3732
          await ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}");
nanahira's avatar
nanahira committed
3733
          CLIENT_kick(client);
mercury233's avatar
mercury233 committed
3734 3735
          return true;
        }
nanahira's avatar
nanahira committed
3736
        ygopro.stoc_send_chat_to_room(room, `${player.name} \${kicked_by_player}`, ygopro.constants.COLORS.RED);
3737 3738 3739 3740 3741
      }
    }
    return false;
  });

nanahira's avatar
nanahira committed
3742
  ygopro.stoc_follow('TYPE_CHANGE', true, async function(buffer, info, client, server, datas) {
神楽坂玲奈's avatar
神楽坂玲奈 committed
3743 3744 3745
    var is_host, selftype;
    selftype = info.type & 0xf;
    is_host = ((info.type >> 4) & 0xf) !== 0;
nanahira's avatar
nanahira committed
3746 3747 3748
    // if room and room.hostinfo.no_watch and selftype == 7
    //   ygopro.stoc_die(client, "${watch_denied_room}")
    //   return true
神楽坂玲奈's avatar
神楽坂玲奈 committed
3749
    client.is_host = is_host;
mercury233's avatar
test3  
mercury233 committed
3750
    client.pos = selftype;
nanahira's avatar
nanahira committed
3751
    //console.log "TYPE_CHANGE to #{client.name}:", info, selftype, is_host
nanahira's avatar
nanahira committed
3752
    return false;
神楽坂玲奈's avatar
神楽坂玲奈 committed
3753 3754
  });

nanahira's avatar
nanahira committed
3755
  ygopro.stoc_follow('HS_PLAYER_ENTER', true, async function(buffer, info, client, server, datas) {
3756 3757
    var pos, room, struct;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3758
    if (!(room && settings.modules.hide_name && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN)) {
3759 3760 3761 3762
      return false;
    }
    pos = info.pos;
    if (pos < 4 && pos !== client.pos) {
3763
      struct = ygopro.structs.get("STOC_HS_PlayerEnter");
3764 3765 3766 3767 3768 3769 3770
      struct._setBuff(buffer);
      struct.set("name", "********");
      buffer = struct.buffer;
    }
    return false;
  });

nanahira's avatar
nanahira committed
3771
  ygopro.stoc_follow('HS_PLAYER_CHANGE', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3772
    var is_ready, j, len, p1, p2, player, pos, possibly_max_player, ref, room;
3773
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3774
    if (!(room && client.pos === 0)) {
3775 3776 3777 3778
      return;
    }
    pos = info.status >> 4;
    is_ready = (info.status & 0xf) === 9;
nanahira's avatar
nanahira committed
3779 3780
    room.ready_player_count = 0;
    room.ready_player_count_without_host = 0;
nanahira's avatar
nanahira committed
3781 3782 3783
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
nanahira's avatar
nanahira committed
3784 3785 3786 3787 3788 3789 3790
      if (player.pos === pos) {
        player.is_ready = is_ready;
      }
      if (player.is_ready) {
        ++room.ready_player_count;
        if (!player.is_host) {
          ++room.ready_player_count_without_host;
3791
        }
nanahira's avatar
nanahira committed
3792 3793 3794 3795 3796 3797 3798 3799 3800 3801
      }
    }
    if (settings.modules.athletic_check.enabled) {
      possibly_max_player = room.hostinfo.mode === 2 ? 4 : 2;
      if (room.ready_player_count >= possibly_max_player) {
        room.check_athletic();
      }
    }
    if (room.max_player && pos < room.max_player) {
      if (room.arena) { // mycard
3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823
        p1 = room.players[0];
        p2 = room.players[1];
        if (!p1 || !p2) {
          if (room.waiting_for_player_interval) {
            clearInterval(room.waiting_for_player_interval);
            room.waiting_for_player_interval = null;
          }
          return;
        }
        room.waiting_for_player2 = room.waiting_for_player;
        room.waiting_for_player = null;
        if (p1.is_ready && p2.is_ready) {
          room.waiting_for_player = p1.is_host ? p1 : p2;
        }
        if (!p1.is_ready && p2.is_ready) {
          room.waiting_for_player = p1;
        }
        if (!p2.is_ready && p1.is_ready) {
          room.waiting_for_player = p2;
        }
        if (room.waiting_for_player !== room.waiting_for_player2) {
          room.waiting_for_player2 = room.waiting_for_player;
nanahira's avatar
nanahira committed
3824
          room.waiting_for_player_time = settings.modules.arena_mode.ready_time;
3825 3826 3827 3828 3829 3830
          room.waiting_for_player_interval = setInterval((function() {
            wait_room_start_arena(ROOM_all[client.rid]);
          }), 1000);
        } else if (!room.waiting_for_player && room.waiting_for_player_interval) {
          clearInterval(room.waiting_for_player_interval);
          room.waiting_for_player_interval = null;
nanahira's avatar
nanahira committed
3831
          room.waiting_for_player_time = settings.modules.arena_mode.ready_time; // random duel
3832 3833 3834
        }
      } else {
        if (room.ready_player_count_without_host >= room.max_player - 1) {
nanahira's avatar
nanahira committed
3835
          //log.info "all ready"
3836
          setTimeout((function() {
nanahira's avatar
nanahira committed
3837
            wait_room_start(ROOM_all[client.rid], settings.modules.random_duel.ready_time);
3838
          }), 1000);
3839 3840 3841 3842 3843
        }
      }
    }
  });

nanahira's avatar
nanahira committed
3844
  ygopro.ctos_follow('REQUEST_FIELD', true, async function(buffer, info, client, server, datas) {
3845 3846 3847
    return true;
  });

nanahira's avatar
nanahira committed
3848
  ygopro.stoc_follow('FIELD_FINISH', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3849 3850
    var room;
    room = ROOM_all[client.rid];
3851
    if (!(room && settings.modules.reconnect.enabled)) {
nanahira's avatar
nanahira committed
3852 3853
      return true;
    }
3854
    client.reconnecting = false;
nanahira's avatar
nanahira committed
3855
    if (client.time_confirm_required) { // client did not send TIME_CONFIRM
3856
      client.waiting_for_last = true;
nanahira's avatar
nanahira committed
3857
    } else if (client.last_game_msg && client.last_game_msg_title !== 'WAITING') { // client sent TIME_CONFIRM
3858 3859 3860 3861
      if (client.last_hint_msg) {
        ygopro.stoc_send(client, 'GAME_MSG', client.last_hint_msg);
      }
      ygopro.stoc_send(client, 'GAME_MSG', client.last_game_msg);
nanahira's avatar
nanahira committed
3862 3863 3864 3865
    }
    return true;
  });

nanahira's avatar
nanahira committed
3866
  ygopro.stoc_follow('DUEL_END', false, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3867
    var j, l, len, len1, player, ref, ref1, room;
nanahira's avatar
move  
nanahira committed
3868 3869 3870 3871
    room = ROOM_all[client.rid];
    if (!(room && settings.modules.replay_delay && room.hostinfo.mode === 1)) {
      return;
    }
3872
    SOCKET_flush_data(client, datas);
nanahira's avatar
move  
nanahira committed
3873
    CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
3874
    if (!room.replays_sent_to_watchers) {
nanahira's avatar
move  
nanahira committed
3875
      room.replays_sent_to_watchers = true;
nanahira's avatar
nanahira committed
3876 3877 3878
      ref = room.players;
      for (j = 0, len = ref.length; j < len; j++) {
        player = ref[j];
nanahira's avatar
js  
nanahira committed
3879 3880 3881 3882
        if (player && player.pos > 3) {
          CLIENT_send_replays(player, room);
        }
      }
nanahira's avatar
nanahira committed
3883 3884 3885
      ref1 = room.watchers;
      for (l = 0, len1 = ref1.length; l < len1; l++) {
        player = ref1[l];
nanahira's avatar
move  
nanahira committed
3886
        if (player) {
nanahira's avatar
nanahira committed
3887
          CLIENT_send_replays(player, room);
nanahira's avatar
move  
nanahira committed
3888 3889 3890 3891 3892
        }
      }
    }
  });

nanahira's avatar
nanahira committed
3893
  wait_room_start = async function(room, time) {
nanahira's avatar
nanahira committed
3894
    var j, len, player, ref;
nanahira's avatar
nanahira committed
3895
    if (room && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.ready_player_count_without_host >= room.max_player - 1) {
3896 3897 3898
      time -= 1;
      if (time) {
        if (!(time % 5)) {
nanahira's avatar
nanahira committed
3899
          ygopro.stoc_send_chat_to_room(room, `${time <= 9 ? ' ' : ''}${time}\${kick_count_down}`, time <= 9 ? ygopro.constants.COLORS.RED : ygopro.constants.COLORS.LIGHTBLUE);
3900 3901 3902 3903 3904
        }
        setTimeout((function() {
          wait_room_start(room, time);
        }), 1000);
      } else {
nanahira's avatar
nanahira committed
3905 3906 3907
        ref = room.players;
        for (j = 0, len = ref.length; j < len; j++) {
          player = ref[j];
mercury233's avatar
fix  
mercury233 committed
3908
          if (player && player.is_host) {
nanahira's avatar
nanahira committed
3909
            await ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}");
nanahira's avatar
nanahira committed
3910
            ygopro.stoc_send_chat_to_room(room, `${player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3911
            CLIENT_kick(player);
3912 3913 3914 3915 3916 3917
          }
        }
      }
    }
  };

nanahira's avatar
nanahira committed
3918
  wait_room_start_arena = async function(room) {
nanahira's avatar
nanahira committed
3919
    var display_name, j, len, player, ref;
nanahira's avatar
nanahira committed
3920
    if (room && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.waiting_for_player) {
3921 3922 3923
      room.waiting_for_player_time = room.waiting_for_player_time - 1;
      if (room.waiting_for_player_time > 0) {
        if (!(room.waiting_for_player_time % 5)) {
nanahira's avatar
nanahira committed
3924 3925 3926
          ref = room.players;
          for (j = 0, len = ref.length; j < len; j++) {
            player = ref[j];
nanahira's avatar
fix  
nanahira committed
3927 3928 3929 3930
            if (!(player)) {
              continue;
            }
            display_name = (settings.modules.hide_name && player !== room.waiting_for_player ? "********" : room.waiting_for_player.name);
nanahira's avatar
nanahira committed
3931
            ygopro.stoc_send_chat(player, `${room.waiting_for_player_time <= 9 ? ' ' : ''}${room.waiting_for_player_time}\${kick_count_down_arena_part1} ${display_name} \${kick_count_down_arena_part2}`, room.waiting_for_player_time <= 9 ? ygopro.constants.COLORS.RED : ygopro.constants.COLORS.LIGHTBLUE);
nanahira's avatar
fix  
nanahira committed
3932
          }
3933 3934
        }
      } else {
nanahira's avatar
nanahira committed
3935
        ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3936
        CLIENT_kick(room.waiting_for_player);
3937 3938 3939 3940 3941 3942 3943 3944
        if (room.waiting_for_player_interval) {
          clearInterval(room.waiting_for_player_interval);
          room.waiting_for_player_interval = null;
        }
      }
    }
  };

nanahira's avatar
nanahira committed
3945
  //tip
nanahira's avatar
nanahira committed
3946
  ygopro.stoc_send_random_tip = async function(client) {
mercury233's avatar
mercury233 committed
3947 3948
    if (settings.modules.tips.enabled && tips.tips.length) {
      ygopro.stoc_send_chat(client, "Tip: " + tips.tips[Math.floor(Math.random() * tips.tips.length)]);
神楽坂玲奈's avatar
神楽坂玲奈 committed
3949
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3950 3951
  };

nanahira's avatar
nanahira committed
3952
  ygopro.stoc_send_random_tip_to_room = async function(room) {
mercury233's avatar
mercury233 committed
3953 3954
    if (settings.modules.tips.enabled && tips.tips.length) {
      ygopro.stoc_send_chat_to_room(room, "Tip: " + tips.tips[Math.floor(Math.random() * tips.tips.length)]);
3955 3956 3957
    }
  };

nanahira's avatar
nanahira committed
3958 3959 3960 3961 3962 3963
  loadRemoteData = global.loadRemoteData = async function(loadObject, name, url) {
    var body, e;
    try {
      body = ((await axios.get(url, {
        responseType: "json"
      }))).data;
mercury233's avatar
mercury233 committed
3964
      if (_.isString(body)) {
nanahira's avatar
nanahira committed
3965 3966
        log.warn(`${name} bad json`, body);
        return false;
mercury233's avatar
mercury233 committed
3967
      }
nanahira's avatar
nanahira committed
3968 3969 3970
      if (!body) {
        log.warn(`${name} empty`, body);
        return false;
nanahira's avatar
nanahira committed
3971
      }
nanahira's avatar
nanahira committed
3972 3973 3974 3975 3976 3977 3978 3979
      await setting_change(loadObject, name, body);
      log.info(`${name} loaded`);
      return true;
    } catch (error1) {
      e = error1;
      log.warn(`${name} error`, e);
      return false;
    }
mercury233's avatar
mercury233 committed
3980 3981
  };

nanahira's avatar
nanahira committed
3982 3983 3984
  load_tips = global.load_tips = async function() {
    return (await loadRemoteData(tips, "tips", settings.modules.tips.get));
  };
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3985

nanahira's avatar
nanahira committed
3986
  ygopro.stoc_follow('DUEL_START', false, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3987
    var deck_arena, deck_name, deck_text, j, l, len, len1, player, ref, ref1, room;
3988
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3989
    if (!(room && !client.reconnecting)) {
3990 3991
      return;
    }
nanahira's avatar
nanahira committed
3992
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) { //first start
nanahira's avatar
nanahira committed
3993
      room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER;
mercury233's avatar
mercury233 committed
3994
      room.start_time = moment().format();
nanahira's avatar
nanahira committed
3995
      room.turn = 0;
mercury233's avatar
mercury233 committed
3996
      if (!room.windbot && settings.modules.http.websocket_roomlist) {
mercury233's avatar
mercury233 committed
3997
        roomlist.start(room);
神楽坂玲奈's avatar
神楽坂玲奈 committed
3998
      }
nanahira's avatar
nanahira committed
3999
      //room.duels = []
4000
      room.dueling_players = [];
nanahira's avatar
nanahira committed
4001 4002 4003
      ref = room.get_playing_player();
      for (j = 0, len = ref.length; j < len; j++) {
        player = ref[j];
4004
        room.dueling_players[player.pos] = player;
4005
        room.scores[player.name_vpass] = 0;
4006
        room.player_datas.push({
4007
          key: CLIENT_get_authorize_key(player),
nanahira's avatar
nanahira committed
4008 4009
          name: player.name,
          pos: player.pos
mercury233's avatar
mercury233 committed
4010
        });
mercury233's avatar
mercury233 committed
4011
        if (room.random_type === 'T') {
nanahira's avatar
nanahira committed
4012
          // 双打房不记录匹配过
mercury233's avatar
mercury233 committed
4013 4014
          ROOM_players_oppentlist[player.ip] = null;
        }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4015
      }
4016
      if (room.hostinfo.auto_death) {
nanahira's avatar
nanahira committed
4017
        ygopro.stoc_send_chat_to_room(room, `\${auto_death_part1}${room.hostinfo.auto_death}\${auto_death_part2}`, ygopro.constants.COLORS.BABYBLUE);
4018
      }
nanahira's avatar
nanahira committed
4019
    } else if (room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING && client.pos < 4) { // side deck verified
nanahira's avatar
nanahira committed
4020 4021 4022 4023 4024 4025
      client.selected_preduel = true;
      if (client.side_tcount) {
        clearInterval(client.side_interval);
        client.side_interval = null;
        client.side_tcount = null;
      }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4026
    }
4027
    if (settings.modules.hide_name && room.duel_count === 0) {
nanahira's avatar
nanahira committed
4028 4029 4030
      ref1 = room.get_playing_player();
      for (l = 0, len1 = ref1.length; l < len1; l++) {
        player = ref1[l];
4031 4032 4033 4034 4035 4036 4037 4038
        if (player !== client) {
          ygopro.stoc_send(client, 'HS_PLAYER_ENTER', {
            name: player.name,
            pos: player.pos
          });
        }
      }
    }
mercury233's avatar
mercury233 committed
4039
    if (settings.modules.tips.enabled) {
mercury233's avatar
test3  
mercury233 committed
4040
      ygopro.stoc_send_random_tip(client);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4041
    }
nanahira's avatar
nanahira committed
4042 4043
    deck_text = null;
    if (client.main && client.main.length) {
mercury233's avatar
mercury233 committed
4044
      deck_text = '#ygopro-server deck log\n#main\n' + client.main.join('\n') + '\n!side\n' + client.side.join('\n') + '\n';
nanahira's avatar
nanahira committed
4045 4046 4047
      room.decks[client.name] = deck_text;
    }
    if (settings.modules.deck_log.enabled && deck_text && !client.deck_saved && !room.windbot) {
mercury233's avatar
mercury233 committed
4048 4049 4050 4051 4052 4053 4054 4055
      deck_arena = settings.modules.deck_log.arena + '-';
      if (room.arena) {
        deck_arena = deck_arena + room.arena;
      } else if (room.hostinfo.mode === 2) {
        deck_arena = deck_arena + 'tag';
      } else if (room.random_type === 'S') {
        deck_arena = deck_arena + 'entertain';
      } else if (room.random_type === 'M') {
mercury233's avatar
mercury233 committed
4056
        deck_arena = deck_arena + 'athletic';
mercury233's avatar
mercury233 committed
4057 4058 4059
      } else {
        deck_arena = deck_arena + 'custom';
      }
nanahira's avatar
nanahira committed
4060
      //log.info "DECK LOG START", client.name, room.arena
mercury233's avatar
mercury233 committed
4061
      if (settings.modules.deck_log.local) {
nanahira's avatar
nanahira committed
4062
        deck_name = moment().format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + client.ip.slice(7) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_');
mercury233's avatar
mercury233 committed
4063
        fs.writeFile(settings.modules.deck_log.local + deck_name + '.ydk', deck_text, 'utf-8', function(err) {
mercury233's avatar
mercury233 committed
4064 4065 4066 4067 4068
          if (err) {
            return log.warn('DECK SAVE ERROR', err);
          }
        });
      }
mercury233's avatar
mercury233 committed
4069
      if (settings.modules.deck_log.post) {
mercury233's avatar
mercury233 committed
4070
        request.post({
mercury233's avatar
mercury233 committed
4071
          url: settings.modules.deck_log.post,
mercury233's avatar
mercury233 committed
4072
          form: {
mercury233's avatar
mercury233 committed
4073
            accesskey: settings.modules.deck_log.accesskey,
mercury233's avatar
mercury233 committed
4074 4075
            deck: deck_text,
            playername: client.name,
mercury233's avatar
mercury233 committed
4076
            arena: deck_arena
mercury233's avatar
mercury233 committed
4077 4078 4079
          }
        }, function(error, response, body) {
          if (error) {
mercury233's avatar
merge  
mercury233 committed
4080
            log.warn('DECK POST ERROR', error);
mercury233's avatar
mercury233 committed
4081 4082
          } else {
            if (response.statusCode !== 200) {
mercury233's avatar
merge  
mercury233 committed
4083
              log.warn('DECK POST FAIL', response.statusCode, client.name, body);
mercury233's avatar
mercury233 committed
4084 4085 4086 4087
            }
          }
        });
      }
nanahira's avatar
nanahira committed
4088 4089
      //else
      //log.info 'DECK POST OK', response.statusCode, client.name, body
mercury233's avatar
mercury233 committed
4090 4091
      client.deck_saved = true;
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4092 4093
  });

nanahira's avatar
nanahira committed
4094
  ygopro.ctos_follow('SURRENDER', true, async function(buffer, info, client, server, datas) {
4095 4096 4097 4098 4099
    var room;
    room = ROOM_all[client.rid];
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
4100
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || room.hostinfo.mode === 2) {
4101 4102
      return true;
    }
4103
    if (room.random_type && room.turn < 3 && !client.flee_free && !settings.modules.test_mode.surrender_anytime && !(room.random_type === 'M' && settings.modules.random_duel.record_match_scores)) {
4104 4105 4106 4107 4108 4109
      ygopro.stoc_send_chat(client, "${surrender_denied}", ygopro.constants.COLORS.BABYBLUE);
      return true;
    }
    return false;
  });

nanahira's avatar
nanahira committed
4110
  report_to_big_brother = global.report_to_big_brother = async function(roomname, sender, ip, level, content, match) {
mercury233's avatar
mercury233 committed
4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135
    if (!settings.modules.big_brother.enabled) {
      return;
    }
    request.post({
      url: settings.modules.big_brother.post,
      form: {
        accesskey: settings.modules.big_brother.accesskey,
        roomname: roomname,
        sender: sender,
        ip: ip,
        level: level,
        content: content,
        match: match
      }
    }, function(error, response, body) {
      if (error) {
        log.warn('BIG BROTHER ERROR', error);
      } else {
        if (response.statusCode !== 200) {
          log.warn('BIG BROTHER FAIL', response.statusCode, roomname, body);
        }
      }
    });
  };

nanahira's avatar
nanahira committed
4136 4137
  //else
  //log.info 'BIG BROTHER OK', response.statusCode, roomname, body
nanahira's avatar
nanahira committed
4138
  ygopro.ctos_follow('CHAT', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4139
    var cancel, ccolor, cip, cmd, cmsg, cname, color, cvalue, msg, name, oldmsg, ref, room, struct, windbot;
4140 4141
    room = ROOM_all[client.rid];
    if (!room) {
mercury233's avatar
mercury233 committed
4142 4143
      return;
    }
mercury233's avatar
mercury233 committed
4144 4145
    msg = _.trim(info.msg);
    cancel = _.startsWith(msg, "/");
4146
    if (!(cancel || !(room.random_type || room.arena) || room.duel_stage === ygopro.constants.DUEL_STAGE.FINGER || room.duel_stage === ygopro.constants.DUEL_STAGE.FIRSTGO || room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING)) {
4147
      room.last_active_time = moment();
4148
    }
mercury233's avatar
mercury233 committed
4149 4150
    cmd = msg.split(' ');
    switch (cmd[0]) {
mercury233's avatar
mercury233 committed
4151 4152
      case '/投降':
      case '/surrender':
nanahira's avatar
nanahira committed
4153
        if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || room.hostinfo.mode === 2) {
mercury233's avatar
mercury233 committed
4154 4155
          return cancel;
        }
4156
        if (room.random_type && room.turn < 3) {
mercury233's avatar
mercury233 committed
4157 4158 4159 4160 4161 4162 4163 4164 4165 4166
          ygopro.stoc_send_chat(client, "${surrender_denied}", ygopro.constants.COLORS.BABYBLUE);
          return cancel;
        }
        if (client.surrend_confirm) {
          ygopro.ctos_send(client.server, 'SURRENDER');
        } else {
          ygopro.stoc_send_chat(client, "${surrender_confirm}", ygopro.constants.COLORS.BABYBLUE);
          client.surrend_confirm = true;
        }
        break;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4167
      case '/help':
mercury233's avatar
mercury233 committed
4168 4169
        ygopro.stoc_send_chat(client, "${chat_order_main}");
        ygopro.stoc_send_chat(client, "${chat_order_help}");
mercury233's avatar
mercury233 committed
4170
        if (!settings.modules.mycard.enabled) {
mercury233's avatar
mercury233 committed
4171
          ygopro.stoc_send_chat(client, "${chat_order_roomname}");
mercury233's avatar
merge  
mercury233 committed
4172
        }
mercury233's avatar
mercury233 committed
4173
        if (settings.modules.windbot.enabled) {
mercury233's avatar
mercury233 committed
4174
          ygopro.stoc_send_chat(client, "${chat_order_windbot}");
mercury233's avatar
mercury233 committed
4175
        }
mercury233's avatar
mercury233 committed
4176
        if (settings.modules.tips.enabled) {
mercury233's avatar
mercury233 committed
4177
          ygopro.stoc_send_chat(client, "${chat_order_tip}");
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4178
        }
nanahira's avatar
nanahira committed
4179 4180 4181 4182 4183 4184
        if (settings.modules.chat_color.enabled) {
          ygopro.stoc_send_chat(client, "${chat_order_chatcolor_1}");
        }
        if (settings.modules.chat_color.enabled) {
          ygopro.stoc_send_chat(client, "${chat_order_chatcolor_2}");
        }
mercury233's avatar
mercury233 committed
4185
        break;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4186
      case '/tip':
mercury233's avatar
mercury233 committed
4187
        if (settings.modules.tips.enabled) {
mercury233's avatar
test3  
mercury233 committed
4188
          ygopro.stoc_send_random_tip(client);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4189
        }
mercury233's avatar
mercury233 committed
4190
        break;
mercury233's avatar
mercury233 committed
4191
      case '/ai':
4192
        if (settings.modules.windbot.enabled && client.is_host && !settings.modules.challonge.enabled && !room.arena && room.random_type !== 'M') {
4193 4194
          cmd.shift();
          if (name = cmd.join(' ')) {
mercury233's avatar
mercury233 committed
4195
            windbot = _.sample(_.filter(windbots, function(w) {
mercury233's avatar
mercury233 committed
4196 4197 4198
              return w.name === name || w.deck === name;
            }));
            if (!windbot) {
mercury233's avatar
mercury233 committed
4199
              ygopro.stoc_send_chat(client, "${windbot_deck_not_found}", ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
4200 4201 4202
              return;
            }
          } else {
4203 4204 4205
            windbot = _.sample(_.filter(windbots, function(w) {
              return !w.hidden;
            }));
mercury233's avatar
mercury233 committed
4206
          }
mercury233's avatar
mercury233 committed
4207 4208 4209
          if (room.random_type) {
            ygopro.stoc_send_chat(client, "${windbot_disable_random_room} " + room.name, ygopro.constants.COLORS.BABYBLUE);
          }
mercury233's avatar
mercury233 committed
4210 4211 4212
          room.add_windbot(windbot);
        }
        break;
mercury233's avatar
mercury233 committed
4213
      case '/roomname':
4214
        if (room) {
mercury233's avatar
mercury233 committed
4215
          ygopro.stoc_send_chat(client, "${room_name} " + room.name, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
4216
        }
nanahira's avatar
nanahira committed
4217 4218 4219
        break;
      case '/color':
        if (settings.modules.chat_color.enabled) {
nanahira's avatar
nanahira committed
4220
          cip = CLIENT_get_authorize_key(client);
nanahira's avatar
nanahira committed
4221 4222 4223
          if (cmsg = cmd[1]) {
            if (cmsg.toLowerCase() === "help") {
              ygopro.stoc_send_chat(client, "${show_color_list}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4224 4225 4226
              ref = ygopro.constants.COLORS;
              for (cname in ref) {
                cvalue = ref[cname];
nanahira's avatar
nanahira committed
4227 4228 4229 4230 4231
                if (cvalue > 10) {
                  ygopro.stoc_send_chat(client, cname, cvalue);
                }
              }
            } else if (cmsg.toLowerCase() === "default") {
nanahira's avatar
fix  
nanahira committed
4232 4233
              chat_color.save_list[cip] = false;
              setting_save(chat_color);
nanahira's avatar
nanahira committed
4234 4235 4236
              ygopro.stoc_send_chat(client, "${set_chat_color_default}", ygopro.constants.COLORS.BABYBLUE);
            } else {
              ccolor = cmsg.toUpperCase();
nanahira's avatar
fix  
nanahira committed
4237 4238 4239
              if (ygopro.constants.COLORS[ccolor] && ygopro.constants.COLORS[ccolor] > 10 && ygopro.constants.COLORS[ccolor] < 20) {
                chat_color.save_list[cip] = ccolor;
                setting_save(chat_color);
nanahira's avatar
nanahira committed
4240 4241 4242 4243 4244 4245
                ygopro.stoc_send_chat(client, "${set_chat_color_part1}" + ccolor + "${set_chat_color_part2}", ygopro.constants.COLORS.BABYBLUE);
              } else {
                ygopro.stoc_send_chat(client, "${color_not_found_part1}" + ccolor + "${color_not_found_part2}", ygopro.constants.COLORS.RED);
              }
            }
          } else {
nanahira's avatar
fix  
nanahira committed
4246
            if (color = chat_color.save_list[cip]) {
nanahira's avatar
nanahira committed
4247 4248 4249 4250 4251 4252
              ygopro.stoc_send_chat(client, "${get_chat_color_part1}" + color + "${get_chat_color_part2}", ygopro.constants.COLORS.BABYBLUE);
            } else {
              ygopro.stoc_send_chat(client, "${get_chat_color_default}", ygopro.constants.COLORS.BABYBLUE);
            }
          }
        }
神楽坂玲奈's avatar
神楽坂玲奈 committed
4253
    }
nanahira's avatar
nanahira committed
4254 4255
    //when '/test'
    //  ygopro.stoc_send_hint_card_to_room(room, 2333365)
mercury233's avatar
mercury233 committed
4256 4257 4258 4259 4260 4261 4262 4263
    if (msg.length > 100) {
      log.warn("SPAM WORD", client.name, client.ip, msg);
      if (client.abuse_count) {
        client.abuse_count = client.abuse_count + 2;
      }
      ygopro.stoc_send_chat(client, "${chat_warn_level0}", ygopro.constants.COLORS.RED);
      cancel = true;
    }
mercury233's avatar
mercury233 committed
4264
    if (!(room && (room.random_type || room.arena))) {
nanahira's avatar
nanahira committed
4265 4266 4267 4268
      if (!cancel && settings.modules.display_watchers && client.is_post_watcher) {
        ygopro.stoc_send_chat_to_room(room, `${client.name}: ${msg}`, 9);
        return true;
      }
mercury233's avatar
mercury233 committed
4269 4270
      return cancel;
    }
nanahira's avatar
nanahira committed
4271
    if (client.abuse_count >= 5 || CLIENT_is_banned_by_mc(client)) {
mercury233's avatar
mercury233 committed
4272
      log.warn("BANNED CHAT", client.name, client.ip, msg);
nanahira's avatar
fix  
nanahira committed
4273
      ygopro.stoc_send_chat(client, "${banned_chat_tip}" + (client.ban_mc && client.ban_mc.message ? ": " + client.ban_mc.message : ""), ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
4274 4275
      return true;
    }
mercury233's avatar
mercury233 committed
4276
    oldmsg = msg;
mercury233's avatar
mercury233 committed
4277
    if (_.any(badwords.level3, function(badword) {
mercury233's avatar
mercury233 committed
4278
      var regexp;
mercury233's avatar
fix  
mercury233 committed
4279
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
4280 4281
      return msg.match(regexp);
    }, msg)) {
mercury233's avatar
mercury233 committed
4282 4283
      log.warn("BAD WORD LEVEL 3", client.name, client.ip, oldmsg, RegExp.$1);
      report_to_big_brother(room.name, client.name, client.ip, 3, oldmsg, RegExp.$1);
mercury233's avatar
mercury233 committed
4284
      cancel = true;
mercury233's avatar
mercury233 committed
4285
      if (client.abuse_count > 0) {
mercury233's avatar
mercury233 committed
4286
        ygopro.stoc_send_chat(client, "${banned_duel_tip}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
4287 4288
        await ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}");
        await ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}", 3);
nanahira's avatar
nanahira committed
4289
        CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
4290
        CLIENT_kick(client);
mercury233's avatar
mercury233 committed
4291 4292 4293
        return true;
      } else {
        client.abuse_count = client.abuse_count + 4;
mercury233's avatar
mercury233 committed
4294
        ygopro.stoc_send_chat(client, "${chat_warn_level2}", ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
4295
      }
nanahira's avatar
nanahira committed
4296
    } else if (client.rag && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
mercury233's avatar
mercury233 committed
4297
      client.rag = false;
nanahira's avatar
nanahira committed
4298
      //ygopro.stoc_send_chat(client, "${chat_warn_level0}", ygopro.constants.COLORS.RED)
mercury233's avatar
mercury233 committed
4299 4300 4301 4302 4303 4304
      cancel = true;
    } else if (_.any(settings.ban.spam_word, function(badword) {
      var regexp;
      regexp = new RegExp(badword, 'i');
      return msg.match(regexp);
    }, msg)) {
nanahira's avatar
nanahira committed
4305
      //log.warn "SPAM WORD", client.name, client.ip, oldmsg
mercury233's avatar
mercury233 committed
4306
      client.abuse_count = client.abuse_count + 2;
mercury233's avatar
mercury233 committed
4307
      ygopro.stoc_send_chat(client, "${chat_warn_level0}", ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
4308
      cancel = true;
mercury233's avatar
mercury233 committed
4309
    } else if (_.any(badwords.level2, function(badword) {
mercury233's avatar
mercury233 committed
4310
      var regexp;
mercury233's avatar
fix  
mercury233 committed
4311
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
4312 4313
      return msg.match(regexp);
    }, msg)) {
mercury233's avatar
mercury233 committed
4314 4315
      log.warn("BAD WORD LEVEL 2", client.name, client.ip, oldmsg, RegExp.$1);
      report_to_big_brother(room.name, client.name, client.ip, 2, oldmsg, RegExp.$1);
mercury233's avatar
mercury233 committed
4316
      client.abuse_count = client.abuse_count + 3;
mercury233's avatar
mercury233 committed
4317
      ygopro.stoc_send_chat(client, "${chat_warn_level2}", ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
4318 4319
      cancel = true;
    } else {
mercury233's avatar
mercury233 committed
4320
      _.each(badwords.level1, function(badword) {
mercury233's avatar
mercury233 committed
4321
        var regexp;
nanahira's avatar
nanahira committed
4322
        //log.info msg
mercury233's avatar
fix  
mercury233 committed
4323
        regexp = new RegExp(badword, "ig");
mercury233's avatar
mercury233 committed
4324 4325 4326
        msg = msg.replace(regexp, "**");
      }, msg);
      if (oldmsg !== msg) {
mercury233's avatar
mercury233 committed
4327 4328
        log.warn("BAD WORD LEVEL 1", client.name, client.ip, oldmsg, RegExp.$1);
        report_to_big_brother(room.name, client.name, client.ip, 1, oldmsg, RegExp.$1);
mercury233's avatar
mercury233 committed
4329
        client.abuse_count = client.abuse_count + 1;
mercury233's avatar
mercury233 committed
4330
        ygopro.stoc_send_chat(client, "${chat_warn_level1}");
4331
        struct = ygopro.structs.get("chat");
mercury233's avatar
mercury233 committed
4332 4333 4334
        struct._setBuff(buffer);
        struct.set("msg", msg);
        buffer = struct.buffer;
mercury233's avatar
mercury233 committed
4335
      } else if (_.any(badwords.level0, function(badword) {
mercury233's avatar
mercury233 committed
4336 4337 4338 4339
        var regexp;
        regexp = new RegExp(badword, 'i');
        return msg.match(regexp);
      }, msg)) {
mercury233's avatar
mercury233 committed
4340 4341
        log.info("BAD WORD LEVEL 0", client.name, client.ip, oldmsg, RegExp.$1);
        report_to_big_brother(room.name, client.name, client.ip, 0, oldmsg, RegExp.$1);
mercury233's avatar
mercury233 committed
4342 4343
      }
    }
mercury233's avatar
mercury233 committed
4344 4345 4346
    if (client.abuse_count >= 2) {
      ROOM_unwelcome(room, client, "${random_ban_reason_abuse}");
    }
mercury233's avatar
mercury233 committed
4347
    if (client.abuse_count >= 5) {
nanahira's avatar
nanahira committed
4348
      ygopro.stoc_send_chat_to_room(room, `${client.name} \${chat_banned}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
4349
      await ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}");
mercury233's avatar
mercury233 committed
4350
    }
nanahira's avatar
nanahira committed
4351 4352 4353 4354
    if (!cancel && settings.modules.display_watchers && client.is_post_watcher) {
      ygopro.stoc_send_chat_to_room(room, `${client.name}: ${msg}`, 9);
      return true;
    }
4355
    return cancel;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4356 4357
  });

nanahira's avatar
nanahira committed
4358
  ygopro.ctos_follow('UPDATE_DECK', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4359
    var 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;
nanahira's avatar
nanahira committed
4360
    if (settings.modules.reconnect.enabled && client.pre_reconnecting) {
nanahira's avatar
nanahira committed
4361 4362 4363 4364
      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);
        CLIENT_kick(client);
      } else if (CLIENT_is_able_to_reconnect(client, buffer)) {
nanahira's avatar
nanahira committed
4365
        CLIENT_reconnect(client);
nanahira's avatar
nanahira committed
4366 4367
      } else if (CLIENT_is_able_to_kick_reconnect(client, buffer)) {
        CLIENT_kick_reconnect(client, buffer);
nanahira's avatar
nanahira committed
4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379
      } else {
        ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED);
        ygopro.stoc_send(client, 'ERROR_MSG', {
          msg: 2,
          code: 0
        });
        ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
          status: (client.pos << 4) | 0xa
        });
      }
      return true;
    }
mercury233's avatar
mercury233 committed
4380 4381 4382 4383
    room = ROOM_all[client.rid];
    if (!room) {
      return false;
    }
nanahira's avatar
nanahira committed
4384 4385
    //log.info info
    if (info.mainc > 256 || info.sidec > 256) { // Prevent attack, see https://github.com/Fluorohydride/ygopro/issues/2174
nanahira's avatar
nanahira committed
4386 4387 4388
      CLIENT_kick(client);
      return true;
    }
mercury233's avatar
mercury233 committed
4389
    buff_main = (function() {
nanahira's avatar
nanahira committed
4390
      var j, ref, results;
4391
      results = [];
nanahira's avatar
nanahira committed
4392
      for (i = j = 0, ref = info.mainc; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
4393
        results.push(info.deckbuf[i]);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4394
      }
4395
      return results;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4396
    })();
mercury233's avatar
mercury233 committed
4397
    buff_side = (function() {
nanahira's avatar
nanahira committed
4398
      var j, ref, ref1, results;
4399
      results = [];
nanahira's avatar
nanahira committed
4400
      for (i = j = ref = info.mainc, ref1 = info.mainc + info.sidec; (ref <= ref1 ? j < ref1 : j > ref1); i = ref <= ref1 ? ++j : --j) {
4401
        results.push(info.deckbuf[i]);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4402
      }
4403
      return results;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4404
    })();
mercury233's avatar
mercury233 committed
4405 4406
    client.main = buff_main;
    client.side = buff_side;
nanahira's avatar
nanahira committed
4407
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
nanahira's avatar
fix  
nanahira committed
4408
      client.start_deckbuf = Buffer.from(buffer);
nanahira's avatar
nanahira committed
4409
    }
nanahira's avatar
nanahira committed
4410
    oppo_pos = room.hostinfo.mode === 2 ? 2 : 1;
nanahira's avatar
nanahira committed
4411
    if (settings.modules.http.quick_death_rule >= 2 && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && room.death && room.scores[room.dueling_players[0].name_vpass] !== room.scores[room.dueling_players[oppo_pos].name_vpass]) {
4412
      win_pos = room.scores[room.dueling_players[0].name_vpass] > room.scores[room.dueling_players[oppo_pos].name_vpass] ? 0 : oppo_pos;
nanahira's avatar
nanahira committed
4413
      room.finished_by_death = true;
nanahira's avatar
nanahira committed
4414
      ygopro.stoc_send_chat_to_room(room, "${death2_finish_part1}" + room.dueling_players[win_pos].name + "${death2_finish_part2}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4415 4416 4417
      if (room.hostinfo.mode === 1) {
        CLIENT_send_replays(room.dueling_players[oppo_pos - win_pos], room);
      }
nanahira's avatar
nanahira committed
4418 4419 4420 4421
      ygopro.stoc_send(room.dueling_players[oppo_pos - win_pos], 'DUEL_END');
      if (room.hostinfo.mode === 2) {
        ygopro.stoc_send(room.dueling_players[oppo_pos - win_pos + 1], 'DUEL_END');
      }
4422
      room.scores[room.dueling_players[oppo_pos - win_pos].name_vpass] = -1;
nanahira's avatar
nanahira committed
4423
      CLIENT_kick(room.dueling_players[oppo_pos - win_pos]);
nanahira's avatar
nanahira committed
4424
      if (room.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
4425
        CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]);
nanahira's avatar
nanahira committed
4426
      }
nanahira's avatar
nanahira committed
4427 4428
      return true;
    }
4429
    struct = ygopro.structs.get("deck");
nanahira's avatar
nanahira committed
4430
    struct._setBuff(buffer);
4431
    if (room.random_type || room.arena) {
nanahira's avatar
nanahira committed
4432
      if (client.pos === 0) {
mercury233's avatar
mercury233 committed
4433 4434 4435
        room.waiting_for_player = room.waiting_for_player2;
      }
      room.last_active_time = moment();
nanahira's avatar
nanahira committed
4436 4437 4438
    }
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.recovering) {
      recover_player_data = _.find(room.recover_duel_log.players, function(player) {
nanahira's avatar
fixes  
nanahira committed
4439
        return player.realName === client.name_vpass && buffer.toString("base64") === player.startDeckBuffer;
nanahira's avatar
nanahira committed
4440
      });
nanahira's avatar
nanahira committed
4441
      if (recover_player_data) {
nanahira's avatar
fixes  
nanahira committed
4442 4443 4444 4445 4446
        recoveredDeck = recover_player_data.getCurrentDeck();
        struct.set("mainc", recoveredDeck.main.length);
        struct.set("sidec", recoveredDeck.side.length);
        struct.set("deckbuf", recoveredDeck.main.concat(recoveredDeck.side));
        if (recover_player_data.isFirst) {
nanahira's avatar
nanahira committed
4447 4448 4449 4450 4451 4452 4453 4454
          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);
      }
nanahira's avatar
nanahira committed
4455
    } 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) {
mercury233's avatar
mercury233 committed
4456 4457 4458 4459 4460 4461
      struct.set("mainc", 1);
      struct.set("sidec", 1);
      struct.set("deckbuf", [4392470, 4392470]);
      buffer = struct.buffer;
      found_deck = false;
      decks = fs.readdirSync(settings.modules.tournament_mode.deck_path);
nanahira's avatar
nanahira committed
4462 4463
      for (j = 0, len = decks.length; j < len; j++) {
        deck = decks[j];
Yuzurisa's avatar
Yuzurisa committed
4464
        if (deck_name_match(deck, client.name)) {
mercury233's avatar
fix  
mercury233 committed
4465 4466
          found_deck = deck;
        }
mercury233's avatar
mercury233 committed
4467 4468
      }
      if (found_deck) {
mercury233's avatar
mercury233 committed
4469
        deck_text = fs.readFileSync(settings.modules.tournament_mode.deck_path + found_deck, {
mercury233's avatar
mercury233 committed
4470 4471 4472 4473 4474 4475
          encoding: "ASCII"
        });
        deck_array = deck_text.split("\n");
        deck_main = [];
        deck_side = [];
        current_deck = deck_main;
nanahira's avatar
nanahira committed
4476 4477
        for (l = 0, len1 = deck_array.length; l < len1; l++) {
          line = deck_array[l];
mercury233's avatar
mercury233 committed
4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491
          if (line.indexOf("!side") >= 0) {
            current_deck = deck_side;
          }
          card = parseInt(line);
          if (!isNaN(card)) {
            current_deck.push(card);
          }
        }
        if (_.isEqual(buff_main, deck_main) && _.isEqual(buff_side, deck_side)) {
          deckbuf = deck_main.concat(deck_side);
          struct.set("mainc", deck_main.length);
          struct.set("sidec", deck_side.length);
          struct.set("deckbuf", deckbuf);
          buffer = struct.buffer;
nanahira's avatar
nanahira committed
4492 4493
          //log.info("deck ok: " + client.name)
          ygopro.stoc_send_chat(client, `\${deck_correct_part1} ${found_deck} \${deck_correct_part2}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
4494
        } else {
nanahira's avatar
nanahira committed
4495 4496
          //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);
mercury233's avatar
mercury233 committed
4497 4498
        }
      } else {
nanahira's avatar
nanahira committed
4499 4500
        //log.info("player deck not found: " + client.name)
        ygopro.stoc_send_chat(client, `${client.name}\${deck_not_found}`, ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
4501
      }
mercury233's avatar
mercury233 committed
4502
    }
mercury233's avatar
mercury233 committed
4503
    return false;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4504 4505
  });

nanahira's avatar
nanahira committed
4506
  ygopro.ctos_follow('RESPONSE', false, async function(buffer, info, client, server, datas) {
4507 4508
    var room;
    room = ROOM_all[client.rid];
4509
    if (!(room && (room.random_type || room.arena))) {
4510 4511
      return;
    }
4512
    room.last_active_time = moment();
4513 4514
  });

nanahira's avatar
nanahira committed
4515
  ygopro.stoc_follow('TIME_LIMIT', true, async function(buffer, info, client, server, datas) {
4516
    var check, cur_players, room;
nanahira's avatar
nanahira committed
4517
    room = ROOM_all[client.rid];
nanahira's avatar
js  
nanahira committed
4518 4519 4520
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548
    check = false;
    if (room.hostinfo.mode !== 2) {
      check = (client.is_first && info.player === 0) || (!client.is_first && info.player === 1);
    } else {
      cur_players = [];
      switch (room.turn % 4) {
        case 1:
          cur_players[0] = 0;
          cur_players[1] = 3;
          break;
        case 2:
          cur_players[0] = 0;
          cur_players[1] = 2;
          break;
        case 3:
          cur_players[0] = 1;
          cur_players[1] = 2;
          break;
        case 0:
          cur_players[0] = 1;
          cur_players[1] = 3;
      }
      if (!room.dueling_players[0].is_first) {
        cur_players[0] = cur_players[0] + 2;
        cur_players[1] = cur_players[1] - 2;
      }
      check = client.pos === cur_players[info.player];
    }
nanahira's avatar
nanahira committed
4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565
    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;
    }
nanahira's avatar
nanahira committed
4566
    if (check) {
4567
      CLIENT_heartbeat_register(client, false);
nanahira's avatar
nanahira committed
4568
    }
nanahira's avatar
js  
nanahira committed
4569
    return false;
nanahira's avatar
nanahira committed
4570 4571
  });

nanahira's avatar
nanahira committed
4572
  ygopro.ctos_follow('TIME_CONFIRM', false, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4573 4574
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4575
    if (!room) {
nanahira's avatar
nanahira committed
4576 4577
      return;
    }
nanahira's avatar
nanahira committed
4578 4579 4580
    if (room.recovered) {
      room.recovered = false;
    }
nanahira's avatar
nanahira committed
4581
    if (settings.modules.reconnect.enabled) {
4582 4583 4584 4585 4586 4587 4588 4589 4590
      if (client.waiting_for_last) {
        client.waiting_for_last = false;
        if (client.last_game_msg && client.last_game_msg_title !== 'WAITING') {
          if (client.last_hint_msg) {
            ygopro.stoc_send(client, 'GAME_MSG', client.last_hint_msg);
          }
          ygopro.stoc_send(client, 'GAME_MSG', client.last_game_msg);
        }
      }
nanahira's avatar
nanahira committed
4591 4592
      client.time_confirm_required = false;
    }
4593 4594 4595 4596
    if (settings.modules.heartbeat_detection.enabled) {
      client.heartbeat_protected = false;
      client.heartbeat_responsed = true;
      CLIENT_heartbeat_unregister(client);
nanahira's avatar
typo  
nanahira committed
4597
    }
nanahira's avatar
nanahira committed
4598 4599
  });

nanahira's avatar
nanahira committed
4600
  ygopro.ctos_follow('HAND_RESULT', false, async function(buffer, info, client, server, datas) {
4601 4602
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4603 4604 4605 4606
    if (!room) {
      return;
    }
    client.selected_preduel = true;
nanahira's avatar
nanahira committed
4607 4608 4609 4610 4611
    if (room.random_type || room.arena) {
      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');
4612 4613 4614
    }
  });

nanahira's avatar
nanahira committed
4615
  ygopro.ctos_follow('TP_RESULT', false, async function(buffer, info, client, server, datas) {
4616 4617
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4618 4619 4620 4621
    if (!room) {
      return;
    }
    client.selected_preduel = true;
nanahira's avatar
nanahira committed
4622
    // room.selecting_tp = false
nanahira's avatar
nanahira committed
4623
    if (!(room.random_type || room.arena)) {
4624 4625
      return;
    }
4626
    room.last_active_time = moment();
4627 4628
  });

nanahira's avatar
nanahira committed
4629
  ygopro.stoc_follow('CHAT', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4630
    var j, len, pid, player, ref, room, tcolor, tplayer;
nanahira's avatar
nanahira committed
4631 4632
    room = ROOM_all[client.rid];
    pid = info.player;
nanahira's avatar
nanahira committed
4633
    if (!(room && pid < 4 && settings.modules.chat_color.enabled && (!settings.modules.hide_name || room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN))) {
nanahira's avatar
nanahira committed
4634 4635
      return;
    }
nanahira's avatar
nanahira committed
4636
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING && !room.dueling_players[0].is_first) {
nanahira's avatar
fix  
nanahira committed
4637
      if (room.hostinfo.mode === 2) {
nanahira's avatar
fix  
nanahira committed
4638 4639 4640 4641 4642 4643
        pid = {
          0: 2,
          1: 3,
          2: 0,
          3: 1
        }[pid];
nanahira's avatar
fix  
nanahira committed
4644 4645 4646 4647
      } else {
        pid = 1 - pid;
      }
    }
nanahira's avatar
nanahira committed
4648 4649 4650
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
nanahira's avatar
nanahira committed
4651 4652 4653 4654 4655 4656 4657
      if (player && player.pos === pid) {
        tplayer = player;
      }
    }
    if (!tplayer) {
      return;
    }
nanahira's avatar
fi  
nanahira committed
4658
    tcolor = chat_color.save_list[CLIENT_get_authorize_key(tplayer)];
nanahira's avatar
nanahira committed
4659 4660 4661 4662 4663 4664 4665 4666 4667
    if (tcolor) {
      ygopro.stoc_send(client, 'CHAT', {
        player: ygopro.constants.COLORS[tcolor],
        msg: tplayer.name + ": " + info.msg
      });
      return true;
    }
  });

nanahira's avatar
nanahira committed
4668
  ygopro.stoc_follow('SELECT_HAND', true, async function(buffer, info, client, server, datas) {
4669 4670
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4671
    if (!room) {
nanahira's avatar
nanahira committed
4672
      return false;
nanahira's avatar
nanahira committed
4673 4674
    }
    if (client.pos === 0) {
nanahira's avatar
nanahira committed
4675
      room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER;
nanahira's avatar
nanahira committed
4676
    }
nanahira's avatar
nanahira committed
4677 4678 4679 4680 4681 4682 4683
    if (room.random_type || room.arena) {
      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');
4684
    }
nanahira's avatar
nanahira committed
4685 4686 4687 4688 4689
    if (room.determine_firstgo) {
      ygopro.ctos_send(server, "HAND_RESULT", {
        res: client.pos === 0 ? 2 : 1
      });
      return true;
4690
    } else {
nanahira's avatar
nanahira committed
4691
      client.selected_preduel = false;
4692
    }
nanahira's avatar
nanahira committed
4693
    return false;
4694 4695
  });

nanahira's avatar
nanahira committed
4696
  ygopro.stoc_follow('HAND_RESULT', true, async function(buffer, info, client, server, datas) {
4697 4698
    var room;
    room = ROOM_all[client.rid];
4699
    if (!room) {
nanahira's avatar
nanahira committed
4700 4701 4702 4703 4704
      return false;
    }
    return room.determine_firstgo;
  });

nanahira's avatar
nanahira committed
4705
  ygopro.stoc_follow('SELECT_TP', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4706 4707 4708 4709
    var room;
    room = ROOM_all[client.rid];
    if (!room) {
      return false;
4710
    }
nanahira's avatar
nanahira committed
4711
    room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO;
mercury233's avatar
fix  
mercury233 committed
4712
    if (room.random_type || room.arena) {
4713 4714 4715
      room.waiting_for_player = client;
      room.last_active_time = moment();
    }
nanahira's avatar
nanahira committed
4716 4717 4718 4719 4720 4721 4722 4723 4724 4725
    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;
4726 4727
  });

nanahira's avatar
nanahira committed
4728
  ygopro.stoc_follow('CHANGE_SIDE', false, async function(buffer, info, client, server, datas) {
nanahira's avatar
js  
nanahira committed
4729
    var room, room_name, sinterval, temp_log;
4730
    room = ROOM_all[client.rid];
4731
    if (!room) {
mercury233's avatar
mercury233 committed
4732 4733
      return;
    }
nanahira's avatar
nanahira committed
4734 4735 4736
    if (client.pos === 0) {
      room.duel_stage = ygopro.constants.DUEL_STAGE.SIDING;
    }
nanahira's avatar
nanahira committed
4737
    client.selected_preduel = false;
nanahira's avatar
nanahira committed
4738 4739
    if (settings.modules.side_timeout) {
      client.side_tcount = settings.modules.side_timeout;
nanahira's avatar
nanahira committed
4740
      ygopro.stoc_send_chat(client, `\${side_timeout_part1}${settings.modules.side_timeout}\${side_timeout_part2}`, ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4741
      sinterval = setInterval(function() {
nanahira's avatar
nanahira committed
4742
        if (!(room && client && client.side_tcount && room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING)) {
nanahira's avatar
nanahira committed
4743 4744 4745 4746
          clearInterval(sinterval);
          return;
        }
        if (client.side_tcount === 1) {
nanahira's avatar
fix  
nanahira committed
4747
          ygopro.stoc_send_chat_to_room(room, client.name + "${side_overtime_room}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4748
          ygopro.stoc_send_chat(client, "${side_overtime}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
4749
          //room.scores[client.name_vpass] = -9
nanahira's avatar
nanahira committed
4750
          CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
4751
          CLIENT_kick(client);
nanahira's avatar
nanahira committed
4752 4753 4754
          return clearInterval(sinterval);
        } else {
          client.side_tcount = client.side_tcount - 1;
nanahira's avatar
nanahira committed
4755
          return ygopro.stoc_send_chat(client, `\${side_remain_part1}${client.side_tcount}\${side_remain_part2}`, ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4756 4757 4758 4759
        }
      }, 60000);
      client.side_interval = sinterval;
    }
nanahira's avatar
js  
nanahira committed
4760
    if (settings.modules.challonge.enabled && settings.modules.challonge.post_score_midduel && room.hostinfo.mode !== 2 && client.pos === 0) {
nanahira's avatar
nanahira committed
4761
      temp_log = JSON.parse(JSON.stringify(room.get_challonge_score()));
nanahira's avatar
nanahira committed
4762
      delete temp_log.winnerId;
nanahira's avatar
js  
nanahira committed
4763
      room_name = room.name;
nanahira's avatar
nanahira committed
4764
      challonge.matches._update({
4765
        id: settings.modules.challonge.tournament_id,
nanahira's avatar
nanahira committed
4766 4767 4768 4769
        matchId: room.challonge_info.id,
        match: temp_log,
        callback: function(err, data) {
          if (err) {
nanahira's avatar
js  
nanahira committed
4770
            log.warn("Errored pushing scores to Challonge.", room_name, err);
nanahira's avatar
nanahira committed
4771 4772
          } else {
            refresh_challonge_cache();
nanahira's avatar
nanahira committed
4773 4774 4775 4776
          }
        }
      });
    }
4777
    if (room.random_type || room.arena) {
nanahira's avatar
nanahira committed
4778
      if (client.pos === 0) {
4779 4780 4781 4782 4783
        room.waiting_for_player = client;
      } else {
        room.waiting_for_player2 = client;
      }
      room.last_active_time = moment();
mercury233's avatar
mercury233 committed
4784 4785 4786
    }
  });

nanahira's avatar
nanahira committed
4787
  ygopro.stoc_follow('REPLAY', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4788
    var i, j, l, len, len1, player, playerInfos, ref, ref1, replay_filename, room;
4789 4790
    room = ROOM_all[client.rid];
    if (!room) {
4791
      return settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player || settings.modules.replay_delay;
4792
    }
nanahira's avatar
nanahira committed
4793
    if (!room.replays[room.duel_count - 1]) {
nanahira's avatar
nanahira committed
4794
      // console.log("Replay saved: ", room.duel_count - 1, client.pos)
4795
      room.replays[room.duel_count - 1] = buffer;
nanahira's avatar
nanahira committed
4796
    }
nanahira's avatar
fixes  
nanahira committed
4797
    if (settings.modules.mysql.enabled) {
nanahira's avatar
nanahira committed
4798
      if (client.pos === 0) {
nanahira's avatar
fixes  
nanahira committed
4799
        replay_filename = moment().format("YYYY-MM-DD HH-mm-ss");
nanahira's avatar
fix  
nanahira committed
4800
        if (room.hostinfo.mode !== 2) {
nanahira's avatar
nanahira committed
4801 4802 4803
          ref = room.dueling_players;
          for (i = j = 0, len = ref.length; j < len; i = ++j) {
            player = ref[i];
nanahira's avatar
nanahira committed
4804 4805 4806
            replay_filename = replay_filename + (i > 0 ? " VS " : " ") + player.name;
          }
        } else {
nanahira's avatar
nanahira committed
4807 4808 4809
          ref1 = room.dueling_players;
          for (i = l = 0, len1 = ref1.length; l < len1; i = ++l) {
            player = ref1[i];
nanahira's avatar
nanahira committed
4810 4811
            replay_filename = replay_filename + (i > 0 ? (i === 2 ? " VS " : " & ") : " ") + player.name;
          }
mercury233's avatar
mercury233 committed
4812 4813
        }
        replay_filename = replay_filename.replace(/[\/\\\?\*]/g, '_') + ".yrp";
nanahira's avatar
fixes  
nanahira committed
4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831
        playerInfos = room.dueling_players.map(function(player) {
          return {
            name: player.name,
            pos: player.pos,
            realName: player.name_vpass,
            startDeckBuffer: player.start_deckbuf,
            deck: {
              main: player.main,
              side: player.side
            },
            isFirst: player.is_first,
            winner: player.pos === room.winner,
            ip: player.ip,
            score: room.scores[player.name_vpass],
            lp: player.lp != null ? player.lp : room.hostinfo.start_lp,
            cardCount: player.card_count != null ? player.card_count : room.hostinfo.start_hand
          };
        });
mercury233's avatar
mercury233 committed
4832 4833 4834 4835 4836
        fs.writeFile(settings.modules.tournament_mode.replay_path + replay_filename, buffer, function(err) {
          if (err) {
            return log.warn("SAVE REPLAY ERROR", replay_filename, err);
          }
        });
nanahira's avatar
fixes  
nanahira committed
4837
        dataManager.saveDuelLog(room.name, room.process_pid.toString(), room.cloud_replay_id, replay_filename, room.hostinfo.mode, room.duel_count, playerInfos); // no synchronize here because too slow
mercury233's avatar
mercury233 committed
4838
      }
nanahira's avatar
fix  
nanahira committed
4839
      if (settings.modules.cloud_replay.enabled && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.replay_safe) {
nanahira's avatar
nanahira committed
4840
        ygopro.stoc_send_chat(client, `\${cloud_replay_delay_part1}R#${room.cloud_replay_id}\${cloud_replay_delay_part2}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
4841
      }
nanahira's avatar
js  
nanahira committed
4842
      return settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player || settings.modules.replay_delay && room.hostinfo.mode === 1;
4843
    } else {
nanahira's avatar
nanahira committed
4844
      return settings.modules.replay_delay && room.hostinfo.mode === 1;
4845 4846 4847
    }
  });

nanahira's avatar
nanahira committed
4848
  // spawn windbot
nanahira's avatar
nanahira committed
4849 4850
  windbot_looplimit = 0;

nanahira's avatar
nanahira committed
4851
  windbot_process = global.windbot_process = null;
nanahira's avatar
nanahira committed
4852

nanahira's avatar
nanahira committed
4853
  spawn_windbot = global.spawn_windbot = function() {
nanahira's avatar
nanahira committed
4854
    var windbot_bin, windbot_parameters;
mercury233's avatar
mercury233 committed
4855 4856 4857 4858 4859 4860 4861 4862 4863 4864
    if (/^win/.test(process.platform)) {
      windbot_bin = 'WindBot.exe';
      windbot_parameters = [];
    } else {
      windbot_bin = 'mono';
      windbot_parameters = ['WindBot.exe'];
    }
    windbot_parameters.push('ServerMode=true');
    windbot_parameters.push('ServerPort=' + settings.modules.windbot.port);
    windbot_process = spawn(windbot_bin, windbot_parameters, {
mercury233's avatar
mercury233 committed
4865 4866 4867 4868
      cwd: 'windbot'
    });
    windbot_process.on('error', function(err) {
      log.warn('WindBot ERROR', err);
nanahira's avatar
nanahira committed
4869
      if (windbot_looplimit < 1000 && !global.rebooted) {
nanahira's avatar
nanahira committed
4870 4871 4872
        windbot_looplimit++;
        spawn_windbot();
      }
mercury233's avatar
mercury233 committed
4873 4874 4875
    });
    windbot_process.on('exit', function(code) {
      log.warn('WindBot EXIT', code);
nanahira's avatar
nanahira committed
4876
      if (windbot_looplimit < 1000 && !global.rebooted) {
nanahira's avatar
nanahira committed
4877 4878 4879
        windbot_looplimit++;
        spawn_windbot();
      }
mercury233's avatar
mercury233 committed
4880 4881 4882 4883 4884
    });
    windbot_process.stdout.setEncoding('utf8');
    windbot_process.stdout.on('data', function(data) {
      log.info('WindBot:', data);
    });
mercury233's avatar
fix  
mercury233 committed
4885
    windbot_process.stderr.setEncoding('utf8');
nanahira's avatar
nanahira committed
4886
    windbot_process.stderr.on('data', function(data) {
mercury233's avatar
mercury233 committed
4887 4888
      log.warn('WindBot Error:', data);
    });
nanahira's avatar
nanahira committed
4889 4890
  };

nanahira's avatar
nanahira committed
4891
  global.rebooted = false;
nanahira's avatar
nanahira committed
4892

nanahira's avatar
nanahira committed
4893
  //http
nanahira's avatar
nanahira committed
4894
  if (true) {
mercury233's avatar
mercury233 committed
4895 4896 4897 4898 4899 4900
    addCallback = function(callback, text) {
      if (!callback) {
        return text;
      }
      return callback + "( " + text + " );";
    };
nanahira's avatar
nanahira committed
4901 4902
    httpRequestListener = async function(request, response) {
      var archive_args, archive_name, archive_process, check, death_room_found, duellog, e, err, error, filename, getpath, j, len, parseQueryString, pass_validated, ref, roomsjson, success, u;
4903 4904
      parseQueryString = true;
      u = url.parse(request.url, parseQueryString);
nanahira's avatar
nanahira committed
4905 4906 4907
      //pass_validated = u.query.pass == settings.modules.http.password

      //console.log(u.query.username, u.query.pass)
4908
      if (u.pathname === '/api/getrooms') {
nanahira's avatar
nanahira committed
4909
        pass_validated = (await auth.auth(u.query.username, u.query.pass, "get_rooms", "get_rooms", true));
nanahira's avatar
fix  
nanahira committed
4910
        if (!settings.modules.http.public_roomlist && !pass_validated) {
4911
          response.writeHead(200);
mercury233's avatar
mercury233 committed
4912
          response.end(addCallback(u.query.callback, '{"rooms":[{"roomid":"0","roomname":"密码错误","needpass":"true"}]}'));
4913
        } else {
nanahira's avatar
nanahira committed
4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926
          roomsjson = [];
          _async.each(ROOM_all, function(room, done) {
            var player;
            if (!(room && room.established)) {
              done();
              return;
            }
            roomsjson.push({
              roomid: room.process_pid.toString(),
              roomname: pass_validated ? room.name : room.name.split('$', 2)[0],
              roommode: room.hostinfo.mode,
              needpass: (room.name.indexOf('$') !== -1).toString(),
              users: _.sortBy((function() {
nanahira's avatar
nanahira committed
4927 4928
                var j, len, ref, results;
                ref = room.players;
nanahira's avatar
nanahira committed
4929
                results = [];
nanahira's avatar
nanahira committed
4930 4931
                for (j = 0, len = ref.length; j < len; j++) {
                  player = ref[j];
nanahira's avatar
nanahira committed
4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944
                  if (player.pos != null) {
                    results.push({
                      id: (-1).toString(),
                      name: player.name,
                      ip: settings.modules.http.show_ip && pass_validated && !player.is_local ? player.ip.slice(7) : null,
                      status: settings.modules.http.show_info && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && player.pos !== 7 ? {
                        score: room.scores[player.name_vpass],
                        lp: player.lp != null ? player.lp : room.hostinfo.start_lp,
                        cards: room.hostinfo.mode !== 2 ? (player.card_count != null ? player.card_count : room.hostinfo.start_hand) : null
                      } : null,
                      pos: player.pos
                    });
                  }
4945
                }
nanahira's avatar
nanahira committed
4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956
                return results;
              })(), "pos"),
              istart: room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN ? (settings.modules.http.show_info ? "Duel:" + room.duel_count + " " + (room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING ? "Siding" : "Turn:" + (room.turn != null ? room.turn : 0) + (room.death ? "/" + (room.death > 0 ? room.death - 1 : "Death") : "")) : 'start') : 'wait'
            });
            return done();
          }, function() {
            response.writeHead(200);
            return response.end(addCallback(u.query.callback, JSON.stringify({
              rooms: roomsjson
            })));
          });
4957
        }
nanahira's avatar
fixes  
nanahira committed
4958
      } else if (u.pathname === '/api/duellog' && settings.modules.mysql.enabled) {
nanahira's avatar
nanahira committed
4959
        if (!(await auth.auth(u.query.username, u.query.pass, "duel_log", "duel_log"))) {
mercury233's avatar
mercury233 committed
4960
          response.writeHead(200);
mercury233's avatar
mercury233 committed
4961
          response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"));
mercury233's avatar
mercury233 committed
4962 4963 4964
          return;
        } else {
          response.writeHead(200);
nanahira's avatar
fixes  
nanahira committed
4965
          duellog = JSON.stringify((await dataManager.getDuelLogJSON(settings.modules.tournament_mode)), null, 2);
mercury233's avatar
mercury233 committed
4966
          response.end(addCallback(u.query.callback, duellog));
mercury233's avatar
mercury233 committed
4967
        }
nanahira's avatar
fixes  
nanahira committed
4968
      } else if (u.pathname === '/api/archive.zip' && settings.modules.mysql.enabled) {
nanahira's avatar
nanahira committed
4969
        if (!(await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay_archive"))) {
nanahira's avatar
nanahira committed
4970 4971 4972 4973 4974
          response.writeHead(403);
          response.end("Invalid password.");
          return;
        } else {
          try {
nanahira's avatar
nanahira committed
4975
            archive_name = moment().format('YYYY-MM-DD HH-mm-ss') + ".zip";
nanahira's avatar
nanahira committed
4976
            archive_args = ["a", "-mx0", "-y", archive_name];
nanahira's avatar
fix  
nanahira committed
4977
            check = false;
nanahira's avatar
nanahira committed
4978 4979 4980
            ref = (await dataManager.getAllReplayFilenames());
            for (j = 0, len = ref.length; j < len; j++) {
              filename = ref[j];
nanahira's avatar
fix  
nanahira committed
4981
              check = true;
nanahira's avatar
fixes  
nanahira committed
4982
              archive_args.push(filename);
nanahira's avatar
nanahira committed
4983
            }
nanahira's avatar
fix  
nanahira committed
4984 4985 4986 4987 4988
            if (!check) {
              response.writeHead(403);
              response.end("Duel logs not found.");
              return;
            }
nanahira's avatar
nanahira committed
4989 4990 4991
            archive_process = spawn(settings.modules.tournament_mode.replay_archive_tool, archive_args, {
              cwd: settings.modules.tournament_mode.replay_path
            });
nanahira's avatar
nanahira committed
4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009
            archive_process.on('error', (err) => {
              response.writeHead(403);
              response.end("Failed packing replays. " + err);
            });
            archive_process.on('exit', (code) => {
              return fs.readFile(settings.modules.tournament_mode.replay_path + archive_name, function(error, buffer) {
                if (error) {
                  response.writeHead(403);
                  response.end("Failed sending replays. " + error);
                } else {
                  response.writeHead(200, {
                    "Content-Type": "application/octet-stream",
                    "Content-Disposition": "attachment"
                  });
                  response.end(buffer);
                }
              });
            });
nanahira's avatar
nanahira committed
5010
            archive_process.stdout.setEncoding('utf8');
nanahira's avatar
nanahira committed
5011 5012 5013
            archive_process.stdout.on('data', (data) => {
              return log.info("archive process: " + data);
            });
nanahira's avatar
nanahira committed
5014
            archive_process.stderr.setEncoding('utf8');
nanahira's avatar
nanahira committed
5015 5016 5017
            archive_process.stderr.on('data', (data) => {
              return log.warn("archive error: " + data);
            });
nanahira's avatar
nanahira committed
5018 5019 5020 5021 5022 5023
          } catch (error1) {
            error = error1;
            response.writeHead(403);
            response.end("Failed reading replays. " + error);
          }
        }
nanahira's avatar
fixes  
nanahira committed
5024
      } else if (u.pathname === '/api/clearlog' && settings.modules.mysql.enabled) {
nanahira's avatar
nanahira committed
5025
        if (!(await auth.auth(u.query.username, u.query.pass, "clear_duel_log", "clear_duel_log"))) {
nanahira's avatar
nanahira committed
5026 5027 5028 5029 5030
          response.writeHead(200);
          response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"));
          return;
        } else {
          response.writeHead(200);
5031
          if (settings.modules.tournament_mode.log_save_path) {
nanahira's avatar
fixes  
nanahira committed
5032
            fs.writeFile(settings.modules.tournament_mode.log_save_path + 'duel_log.' + moment().format('YYYY-MM-DD HH-mm-ss') + '.json', JSON.stringify((await dataManager.getDuelLogJSON(settings.modules.tournament_mode)), null, 2), function(err) {
5033 5034 5035 5036 5037
              if (err) {
                return log.warn('DUEL LOG SAVE ERROR', err);
              }
            });
          }
nanahira's avatar
fixes  
nanahira committed
5038
          await dataManager.clearDuelLog();
nanahira's avatar
nanahira committed
5039 5040
          response.end(addCallback(u.query.callback, "[{name:'Success'}]"));
        }
nanahira's avatar
fixes  
nanahira committed
5041
      } else if (_.startsWith(u.pathname, '/api/replay') && settings.modules.mysql.enabled) {
nanahira's avatar
nanahira committed
5042
        if (!(await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay"))) {
mercury233's avatar
mercury233 committed
5043 5044 5045 5046
          response.writeHead(403);
          response.end("密码错误");
          return;
        } else {
nanahira's avatar
nanahira committed
5047 5048 5049 5050 5051 5052 5053 5054 5055 5056
          getpath = null;
          filename = null;
          try {
            getpath = u.pathname.split("/");
            filename = path.basename(decodeURIComponent(getpath.pop()));
          } catch (error1) {
            response.writeHead(404);
            response.end("bad filename");
            return;
          }
mercury233's avatar
mercury233 committed
5057 5058 5059
          fs.readFile(settings.modules.tournament_mode.replay_path + filename, function(error, buffer) {
            if (error) {
              response.writeHead(404);
mercury233's avatar
fix  
mercury233 committed
5060
              response.end("未找到文件 " + filename);
mercury233's avatar
mercury233 committed
5061 5062 5063 5064 5065
            } else {
              response.writeHead(200, {
                "Content-Type": "application/octet-stream",
                "Content-Disposition": "attachment"
              });
mercury233's avatar
fix  
mercury233 committed
5066
              response.end(buffer);
mercury233's avatar
mercury233 committed
5067 5068 5069
            }
          });
        }
5070
      } else if (u.pathname === '/api/message') {
nanahira's avatar
nanahira committed
5071 5072 5073 5074
        //if !pass_validated
        //  response.writeHead(200)
        //  response.end(addCallback(u.query.callback, "['密码错误', 0]"))
        //  return
5075
        if (u.query.shout) {
nanahira's avatar
nanahira committed
5076
          if (!(await auth.auth(u.query.username, u.query.pass, "shout", "shout"))) {
nanahira's avatar
nanahira committed
5077 5078 5079 5080
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5081
          _async.each(ROOM_all, function(room) {
5082
            if (room && room.established) {
nanahira's avatar
nanahira committed
5083
              return ygopro.stoc_send_chat_to_room(room, u.query.shout, ygopro.constants.COLORS.YELLOW);
5084
            }
nanahira's avatar
nanahira committed
5085
          });
5086
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5087
          response.end(addCallback(u.query.callback, "['shout ok', '" + u.query.shout + "']"));
5088
        } else if (u.query.stop) {
nanahira's avatar
nanahira committed
5089
          if (!(await auth.auth(u.query.username, u.query.pass, "stop", "stop"))) {
nanahira's avatar
nanahira committed
5090 5091 5092 5093
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
5094 5095 5096
          if (u.query.stop === 'false') {
            u.query.stop = false;
          }
nanahira's avatar
nanahira committed
5097 5098
          response.writeHead(200);
          try {
nanahira's avatar
fixes  
nanahira committed
5099
            await setting_change(settings, 'modules:stop', u.query.stop);
nanahira's avatar
nanahira committed
5100 5101 5102 5103 5104
            response.end(addCallback(u.query.callback, "['stop ok', '" + u.query.stop + "']"));
          } catch (error1) {
            err = error1;
            response.end(addCallback(u.query.callback, "['stop fail', '" + u.query.stop + "']"));
          }
5105
        } else if (u.query.welcome) {
nanahira's avatar
nanahira committed
5106
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "change_welcome"))) {
nanahira's avatar
nanahira committed
5107 5108 5109 5110
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5111
          try {
nanahira's avatar
fixes  
nanahira committed
5112
            await setting_change(settings, 'modules:welcome', u.query.welcome);
nanahira's avatar
nanahira committed
5113 5114 5115 5116 5117
            response.end(addCallback(u.query.callback, "['welcome ok', '" + u.query.welcome + "']"));
          } catch (error1) {
            err = error1;
            response.end(addCallback(u.query.callback, "['welcome fail', '" + u.query.welcome + "']"));
          }
mercury233's avatar
mercury233 committed
5118
        } else if (u.query.getwelcome) {
nanahira's avatar
nanahira committed
5119
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "get_welcome"))) {
nanahira's avatar
nanahira committed
5120 5121 5122 5123
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
5124
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5125
          response.end(addCallback(u.query.callback, "['get ok', '" + settings.modules.welcome + "']"));
mercury233's avatar
mercury233 committed
5126
        } else if (u.query.loadtips) {
nanahira's avatar
nanahira committed
5127
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "change_tips"))) {
nanahira's avatar
nanahira committed
5128 5129 5130 5131
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5132 5133 5134 5135 5136 5137 5138
          success = (await load_tips());
          response.writeHead(200);
          if (success) {
            response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']"));
          } else {
            response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"));
          }
mercury233's avatar
mercury233 committed
5139
        } else if (u.query.loaddialogues) {
nanahira's avatar
nanahira committed
5140
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues"))) {
nanahira's avatar
nanahira committed
5141 5142 5143 5144
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5145 5146 5147 5148 5149 5150 5151
          success = (await load_tips());
          response.writeHead(200);
          if (success) {
            response.end(addCallback(u.query.callback, "['dialogue ok', '" + settings.modules.tips.get + "']"));
          } else {
            response.end(addCallback(u.query.callback, "['dialogue fail', '" + settings.modules.tips.get + "']"));
          }
mercury233's avatar
mercury233 committed
5152
        } else if (u.query.ban) {
nanahira's avatar
nanahira committed
5153
          if (!(await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user"))) {
nanahira's avatar
nanahira committed
5154 5155 5156 5157
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5158 5159 5160 5161 5162
          try {
            await ban_user(u.query.ban);
          } catch (error1) {
            e = error1;
            log.warn("ban fail", e.toString());
nanahira's avatar
nanahira committed
5163
            response.writeHead(200);
nanahira's avatar
nanahira committed
5164 5165 5166 5167 5168
            response.end(addCallback(u.query.callback, "['ban fail', '" + u.query.ban + "']"));
            return;
          }
          response.writeHead(200);
          response.end(addCallback(u.query.callback, "['ban ok', '" + u.query.ban + "']"));
nanahira's avatar
nanahira committed
5169
        } else if (u.query.kick) {
nanahira's avatar
nanahira committed
5170
          if (!(await auth.auth(u.query.username, u.query.pass, "kick_user", "kick_user"))) {
nanahira's avatar
nanahira committed
5171 5172 5173 5174
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5175 5176 5177 5178 5179 5180 5181 5182
          ROOM_kick(u.query.kick, function(err, found) {
            response.writeHead(200);
            if (err) {
              return response.end(addCallback(u.query.callback, "['kick fail', '" + u.query.kick + "']"));
            } else if (found) {
              return response.end(addCallback(u.query.callback, "['kick ok', '" + u.query.kick + "']"));
            } else {
              return response.end(addCallback(u.query.callback, "['room not found', '" + u.query.kick + "']"));
nanahira's avatar
nanahira committed
5183
            }
nanahira's avatar
nanahira committed
5184
          });
nanahira's avatar
nanahira committed
5185
        } else if (u.query.death) {
nanahira's avatar
nanahira committed
5186
          if (!(await auth.auth(u.query.username, u.query.pass, "start_death", "start_death"))) {
nanahira's avatar
nanahira committed
5187 5188 5189 5190
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5191
          death_room_found = false;
nanahira's avatar
nanahira committed
5192 5193 5194 5195 5196
          _async.each(ROOM_all, function(room, done) {
            if (!(room && (u.query.death === "all" || u.query.death === room.process_pid.toString() || u.query.death === room.name))) {
              done();
              return;
            }
nanahira's avatar
nanahira committed
5197 5198
            if (room.start_death()) {
              death_room_found = true;
nanahira's avatar
nanahira committed
5199
            }
nanahira's avatar
nanahira committed
5200
            done();
nanahira's avatar
nanahira committed
5201 5202 5203 5204 5205 5206 5207 5208
          }, function() {
            response.writeHead(200);
            if (death_room_found) {
              return response.end(addCallback(u.query.callback, "['death ok', '" + u.query.death + "']"));
            } else {
              return response.end(addCallback(u.query.callback, "['room not found', '" + u.query.death + "']"));
            }
          });
nanahira's avatar
nanahira committed
5209
        } else if (u.query.deathcancel) {
nanahira's avatar
nanahira committed
5210
          if (!(await auth.auth(u.query.username, u.query.pass, "start_death", "cancel_death"))) {
nanahira's avatar
nanahira committed
5211 5212 5213 5214
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5215
          death_room_found = false;
nanahira's avatar
nanahira committed
5216
          _async.each(rooms, function(room, done) {
nanahira's avatar
nanahira committed
5217 5218 5219 5220
            if (!(room && (u.query.deathcancel === "all" || u.query.deathcancel === room.process_pid.toString() || u.query.deathcancel === room.name))) {
              done();
              return;
            }
nanahira's avatar
nanahira committed
5221 5222
            if (room.cancel_death()) {
              death_room_found = true;
nanahira's avatar
nanahira committed
5223
            }
nanahira's avatar
nanahira committed
5224 5225 5226 5227 5228 5229 5230 5231 5232
            return done();
          }, function() {
            response.writeHead(200);
            if (death_room_found) {
              return response.end(addCallback(u.query.callback, "['death cancel ok', '" + u.query.deathcancel + "']"));
            } else {
              return response.end(addCallback(u.query.callback, "['room not found', '" + u.query.deathcancel + "']"));
            }
          });
nanahira's avatar
nanahira committed
5233
        } else if (u.query.reboot) {
nanahira's avatar
nanahira committed
5234
          if (!(await auth.auth(u.query.username, u.query.pass, "stop", "reboot"))) {
nanahira's avatar
nanahira committed
5235 5236 5237 5238
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5239 5240 5241 5242
          ROOM_kick("all", function(err, found) {
            global.rebooted = true;
            if (windbot_process) {
              windbot_process.kill();
nanahira's avatar
nanahira committed
5243
            }
nanahira's avatar
nanahira committed
5244 5245 5246 5247
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['reboot ok', '" + u.query.reboot + "']"));
            return process.exit();
          });
5248
        } else {
mercury233's avatar
mercury233 committed
5249
          response.writeHead(400);
5250
          response.end();
5251
        }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
5252
      } else {
mercury233's avatar
mercury233 committed
5253
        response.writeHead(400);
mercury233's avatar
test3  
mercury233 committed
5254
        response.end();
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
5255
      }
5256
    };
nanahira's avatar
nanahira committed
5257 5258
  }

nanahira's avatar
nanahira committed
5259
  init();
nanahira's avatar
nanahira committed
5260

神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
5261
}).call(this);