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, windbots, 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 222
  windbots = [];

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

nanahira's avatar
nanahira committed
225
  challonge = null;
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
226

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

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

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

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

nanahira's avatar
nanahira committed
244 245 246
  replaced_index = function() {};

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

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

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

    };

    ResolveData.prototype.resolved = false;

    return ResolveData;

  }).call(this);

nanahira's avatar
fix  
nanahira committed
271
  loadLFList = async function(path) {
nanahira's avatar
nanahira committed
272 273
    var date, j, len, list, ref, results;
    try {
nanahira's avatar
fix  
nanahira committed
274
      ref = ((await fs.promises.readFile(path, 'utf8'))).match(/!.*/g);
nanahira's avatar
nanahira committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
      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() {
nanahira's avatar
nanahira committed
294
    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;
nanahira's avatar
nanahira committed
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
    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);
    }
nanahira's avatar
nanahira committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
    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.");
      }
    }
nanahira's avatar
nanahira committed
421 422 423 424 425 426 427
    // 读取数据
    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);
428
    }
nanahira's avatar
nanahira committed
429 430 431 432 433 434 435 436 437 438 439 440
    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);
    }
nanahira's avatar
nanahira committed
441
    if (settings.modules.chat_color.enabled && (await checkFileExists('./config/chat_color.json'))) {
nanahira's avatar
nanahira committed
442
      try {
nanahira's avatar
nanahira committed
443 444 445
        chat_color = (await loadJSONAsync('./config/chat_color.json'));
        if (chat_color) {
          await dataManager.migrateChatColors(chat_color.save_list);
nanahira's avatar
nanahira committed
446
          await fs.promises.rename('./config/chat_color.json', './config/chat_color.json.bak');
nanahira's avatar
nanahira committed
447 448
          log.info("Chat color migrated.");
        }
nanahira's avatar
nanahira committed
449
      } catch (error1) {
nanahira's avatar
nanahira committed
450

nanahira's avatar
nanahira committed
451 452 453 454 455 456 457 458 459 460 461 462 463 464
      }
    }
    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) {
nanahira's avatar
nanahira committed
465
      windbots = global.windbots = ((await loadJSONAsync(settings.modules.windbot.botlist))).windbots;
nanahira's avatar
nanahira committed
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 523 524 525 526 527 528
      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.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
529 530 531 532
      challonge_cache = {
        participants: null,
        matches: null
      };
nanahira's avatar
nanahira committed
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
      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();
nanahira's avatar
fix  
nanahira committed
602 603
    }
    if (settings.modules.tips.enabled) {
nanahira's avatar
nanahira committed
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
      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) {
nanahira's avatar
nanahira committed
620
      setInterval(async function() {
nanahira's avatar
nanahira committed
621 622 623 624 625 626 627 628 629
        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);
nanahira's avatar
nanahira committed
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
        try {
          //log.info scores
          await axios.post(settings.modules.random_duel.post_match_scores, {
            headers: {
              'content-type': 'application/x-www-form-urlencoded'
            },
            data: qs.stringify({
              accesskey: settings.modules.random_duel.post_match_accesskey,
              rank: JSON.stringify(scores),
              responseType: "json"
            })
          });
        } catch (error1) {
          e = error1;
          log.warn('RANDOM SCORE POST ERROR', e.toString());
        }
nanahira's avatar
nanahira committed
646
      }, 60000);
nanahira's avatar
nanahira committed
647
    }
nanahira's avatar
nanahira committed
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
    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
670
        }
nanahira's avatar
nanahira committed
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
      }, 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
692
          }
nanahira's avatar
js  
nanahira committed
693
        }
nanahira's avatar
nanahira committed
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
        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
713
    }
nanahira's avatar
nanahira committed
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 740 741 742 743 744
    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
745
      }
nanahira's avatar
nanahira committed
746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
      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
765
      }
nanahira's avatar
nanahira committed
766
      https_server.listen(settings.modules.http.ssl.port);
nanahira's avatar
nanahira committed
767
    }
nanahira's avatar
nanahira committed
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"));
    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);
nanahira's avatar
nanahira committed
778
      log.info("Plugin loaded:", plugin_filename);
nanahira's avatar
nanahira committed
779 780
    }
  };
nanahira's avatar
nanahira committed
781

nanahira's avatar
nanahira committed
782
  // 获取可用内存
nanahira's avatar
nanahira committed
783
  memory_usage = global.memory_usage = 0;
mercury233's avatar
mercury233 committed
784

nanahira's avatar
nanahira committed
785 786
  get_memory_usage = global.get_memory_usage = function() {
    var percentUsed;
nanahira's avatar
fix  
nanahira committed
787
    percentUsed = os.freemem() / os.totalmem() * 100;
nanahira's avatar
nanahira committed
788
    memory_usage = global.memory_usage = percentUsed;
mercury233's avatar
mercury233 committed
789 790
  };

mercury233's avatar
mercury233 committed
791 792 793 794
  get_memory_usage();

  setInterval(get_memory_usage, 3000);

nanahira's avatar
nanahira committed
795
  ROOM_all = global.ROOM_all = [];
mercury233's avatar
mercury233 committed
796

nanahira's avatar
nanahira committed
797
  ROOM_players_oppentlist = global.ROOM_players_oppentlist = {};
mercury233's avatar
mercury233 committed
798

nanahira's avatar
nanahira committed
799
  ROOM_players_scores = global.ROOM_players_scores = {};
800

nanahira's avatar
nanahira committed
801
  ROOM_connected_ip = global.ROOM_connected_ip = {};
mercury233's avatar
mercury233 committed
802

nanahira's avatar
nanahira committed
803
  ROOM_bad_ip = global.ROOM_bad_ip = {};
mercury233's avatar
mercury233 committed
804

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

nanahira's avatar
nanahira committed
841
  // automatically ban user to use random duel
nanahira's avatar
nanahira committed
842 843
  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) {
844 845
      return;
    }
nanahira's avatar
nanahira committed
846
    await dataManager.randomDuelBanPlayer(ip, reason, countadd);
mercury233's avatar
mercury233 committed
847 848
  };

nanahira's avatar
nanahira committed
849
  ROOM_kick = function(name, callback) {
nanahira's avatar
nanahira committed
850 851 852 853 854 855 856 857
    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
858
      room.terminate();
nanahira's avatar
nanahira committed
859
      return done();
nanahira's avatar
nanahira committed
860
    }, function(err) {
nanahira's avatar
nanahira committed
861
      callback(null, found);
nanahira's avatar
nanahira committed
862 863 864
    });
  };

nanahira's avatar
nanahira committed
865
  ROOM_player_win = global.ROOM_player_win = function(name) {
866 867 868 869 870 871 872 873 874 875 876 877
    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
878
  ROOM_player_lose = global.ROOM_player_lose = function(name) {
879 880 881 882 883 884 885 886 887 888 889 890
    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
891
  ROOM_player_flee = global.ROOM_player_flee = function(name) {
892 893 894 895 896 897 898 899 900 901 902 903
    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
904
  ROOM_player_get_score = global.ROOM_player_get_score = function(player) {
905 906 907 908
    var name, score, total;
    name = player.name_vpass;
    score = ROOM_players_scores[name];
    if (!score) {
nanahira's avatar
nanahira committed
909
      return `${player.name} \${random_score_blank}`;
910
    }
mercury233's avatar
mercury233 committed
911
    total = score.win + score.lose;
912
    if (score.win < 2 && total < 3) {
nanahira's avatar
nanahira committed
913
      return `${player.name} \${random_score_not_enough}`;
914 915
    }
    if (score.combo >= 2) {
nanahira's avatar
nanahira committed
916
      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}`;
917
    } else {
nanahira's avatar
nanahira committed
918 919
      //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}`;
920 921 922
    }
  };

nanahira's avatar
nanahira committed
923
  ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = async function(name, player_ip) {
nanahira's avatar
fixes  
nanahira committed
924
    var room, success, uname;
mercury233's avatar
mercury233 committed
925
    uname = name.toUpperCase();
mercury233's avatar
mercury233 committed
926
    if (settings.modules.windbot.enabled && (uname.slice(0, 2) === 'AI' || (!settings.modules.random_duel.enabled && uname === ''))) {
mercury233's avatar
mercury233 committed
927 928
      return ROOM_find_or_create_ai(name);
    }
mercury233's avatar
mercury233 committed
929
    if (settings.modules.random_duel.enabled && (uname === '' || uname === 'S' || uname === 'M' || uname === 'T')) {
nanahira's avatar
nanahira committed
930
      return (await ROOM_find_or_create_random(uname, player_ip));
mercury233's avatar
mercury233 committed
931 932 933
    }
    if (room = ROOM_find_by_name(name)) {
      return room;
mercury233's avatar
mercury233 committed
934
    } else if (memory_usage >= 90) {
mercury233's avatar
mercury233 committed
935 936
      return null;
    } else {
nanahira's avatar
fixes  
nanahira committed
937 938 939 940 941 942 943 944 945 946
      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
947 948 949
    }
  };

nanahira's avatar
nanahira committed
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
  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
976 977 978
      }
    }
    max_player = type === 'T' ? 4 : 2;
nanahira's avatar
nanahira committed
979
    playerbanned = randomDuelBanRecord && randomDuelBanRecord.count > 3 && moment() < randomDuelBanRecord.time;
mercury233's avatar
mercury233 committed
980
    result = _.find(ROOM_all, function(room) {
981
      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
982 983
    });
    if (result) {
mercury233's avatar
mercury233 committed
984
      result.welcome = '${random_duel_enter_room_waiting}';
nanahira's avatar
nanahira committed
985
    //log.info 'found room', player_name
mercury233's avatar
mercury233 committed
986
    } else if (memory_usage < 90) {
987
      type = type ? type : settings.modules.random_duel.default_type;
mercury233's avatar
mercury233 committed
988 989 990 991
      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
992
      result.welcome = '${random_duel_enter_room_new}';
mercury233's avatar
mercury233 committed
993
      result.deprecated = playerbanned;
mercury233's avatar
mercury233 committed
994
    } else {
nanahira's avatar
nanahira committed
995
      //log.info 'create room', player_name, name
mercury233's avatar
mercury233 committed
996
      return null;
mercury233's avatar
mercury233 committed
997
    }
998 999 1000
    if (result.random_type === 'S') {
      result.welcome2 = '${random_duel_enter_room_single}';
    }
mercury233's avatar
mercury233 committed
1001
    if (result.random_type === 'M') {
1002 1003 1004 1005
      result.welcome2 = '${random_duel_enter_room_match}';
    }
    if (result.random_type === 'T') {
      result.welcome2 = '${random_duel_enter_room_tag}';
mercury233's avatar
mercury233 committed
1006 1007 1008 1009
    }
    return result;
  };

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

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

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

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

nanahira's avatar
nanahira committed
1075 1076 1077 1078 1079 1080
  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
1081
  ROOM_validate = global.ROOM_validate = function(name) {
mercury233's avatar
mercury233 committed
1082 1083 1084 1085 1086 1087 1088 1089 1090
    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;
1091 1092 1093
      if (!room) {
        return false;
      }
mercury233's avatar
mercury233 committed
1094 1095 1096 1097 1098 1099 1100
      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
1101
  ROOM_unwelcome = global.ROOM_unwelcome = function(room, bad_player, reason) {
nanahira's avatar
nanahira committed
1102
    var j, len, player, ref;
mercury233's avatar
mercury233 committed
1103 1104 1105
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
1106 1107 1108
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
mercury233's avatar
mercury233 committed
1109
      if (player && player === bad_player) {
nanahira's avatar
nanahira committed
1110
        ygopro.stoc_send_chat(player, `\${unwelcome_warn_part1}${reason}\${unwelcome_warn_part2}`, ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
1111 1112
      } else if (player && player.pos !== 7 && player !== bad_player) {
        player.flee_free = true;
nanahira's avatar
nanahira committed
1113
        ygopro.stoc_send_chat(player, `\${unwelcome_tip_part1}${reason}\${unwelcome_tip_part2}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
1114 1115 1116 1117
      }
    }
  };

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

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

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

nanahira's avatar
nanahira committed
1158
  CLIENT_get_authorize_key = global.CLIENT_get_authorize_key = function(client) {
nanahira's avatar
js  
nanahira committed
1159
    if (!settings.modules.mycard.enabled && client.vpass) {
1160
      return client.name_vpass;
nanahira's avatar
js  
nanahira committed
1161 1162
    } 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
1163
    } else {
1164
      return client.ip + ":" + client.name;
nanahira's avatar
nanahira committed
1165 1166 1167
    }
  };

nanahira's avatar
nanahira committed
1168
  CLIENT_reconnect_unregister = global.CLIENT_reconnect_unregister = function(client, reconnected, exact) {
nanahira's avatar
nanahira committed
1169 1170 1171 1172
    if (!settings.modules.reconnect.enabled) {
      return false;
    }
    if (disconnect_list[CLIENT_get_authorize_key(client)]) {
nanahira's avatar
nanahira committed
1173 1174 1175
      if (exact && disconnect_list[CLIENT_get_authorize_key(client)].old_client !== client) {
        return false;
      }
nanahira's avatar
nanahira committed
1176 1177 1178 1179 1180 1181 1182
      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
1183
  CLIENT_reconnect_register = global.CLIENT_reconnect_register = function(client, room_id, error) {
nanahira's avatar
nanahira committed
1184
    var dinfo, room, tmot;
nanahira's avatar
nanahira committed
1185
    room = ROOM_all[room_id];
nanahira's avatar
nanahira committed
1186 1187 1188
    if (client.had_new_reconnection) {
      return false;
    }
nanahira's avatar
nanahira committed
1189
    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
1190 1191
      return false;
    }
nanahira's avatar
nanahira committed
1192 1193 1194
    // 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
1195
    dinfo = {
nanahira's avatar
nanahira committed
1196
      room_id: room_id,
nanahira's avatar
nanahira committed
1197 1198 1199 1200 1201 1202
      old_client: client,
      old_server: client.server,
      deckbuf: client.start_deckbuf
    };
    tmot = setTimeout(function() {
      room.disconnect(client, error);
nanahira's avatar
nanahira committed
1203
    //SERVER_kick(dinfo.old_server)
nanahira's avatar
nanahira committed
1204 1205 1206
    }, settings.modules.reconnect.wait_time);
    dinfo.timeout = tmot;
    disconnect_list[CLIENT_get_authorize_key(client)] = dinfo;
nanahira's avatar
nanahira committed
1207 1208
    //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
1209 1210 1211 1212
    if (client.time_confirm_required) {
      client.time_confirm_required = false;
      ygopro.ctos_send(client.server, 'TIME_CONFIRM');
    }
nanahira's avatar
nanahira committed
1213
    if (settings.modules.reconnect.auto_surrender_after_disconnect && room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
1214 1215
      ygopro.ctos_send(client.server, 'SURRENDER');
    }
nanahira's avatar
nanahira committed
1216 1217 1218
    return true;
  };

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

nanahira's avatar
nanahira committed
1249
  SERVER_clear_disconnect = global.SERVER_clear_disconnect = function(server) {
nanahira's avatar
nanahira committed
1250 1251
    var k, v;
    if (!settings.modules.reconnect.enabled) {
nanahira's avatar
nanahira committed
1252
      return false;
nanahira's avatar
nanahira committed
1253 1254 1255 1256 1257 1258
    }
    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
1259
        return true;
nanahira's avatar
nanahira committed
1260 1261
      }
    }
nanahira's avatar
nanahira committed
1262 1263 1264
    return false;
  };

nanahira's avatar
nanahira committed
1265
  ROOM_clear_disconnect = global.ROOM_clear_disconnect = function(room_id) {
nanahira's avatar
nanahira committed
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278
    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
1279 1280
  };

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

nanahira's avatar
nanahira committed
1295
  CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1296
    var disconnect_info, room;
nanahira's avatar
nanahira committed
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
    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
1307 1308 1309 1310 1311
    room = ROOM_all[disconnect_info.room_id];
    if (!room) {
      CLIENT_reconnect_unregister(client);
      return false;
    }
nanahira's avatar
nanahira committed
1312
    if (deckbuf && deckbuf.compare(disconnect_info.deckbuf) !== 0) {
nanahira's avatar
nanahira committed
1313 1314 1315 1316 1317
      return false;
    }
    return true;
  };

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

nanahira's avatar
nanahira committed
1335
  CLIENT_is_able_to_kick_reconnect = global.CLIENT_is_able_to_kick_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1336 1337 1338 1339 1340 1341
    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
1342 1343 1344
    return true;
  };

nanahira's avatar
nanahira committed
1345
  CLIENT_send_pre_reconnect_info = global.CLIENT_send_pre_reconnect_info = function(client, room, old_client) {
nanahira's avatar
nanahira committed
1346
    var j, len, player, ref, req_pos;
nanahira's avatar
nanahira committed
1347
    ygopro.stoc_send_chat(client, "${pre_reconnecting_to_room}", ygopro.constants.COLORS.BABYBLUE);
1348
    ygopro.stoc_send(client, 'JOIN_GAME', room.join_game_buffer);
nanahira's avatar
nanahira committed
1349 1350 1351 1352 1353 1354 1355
    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
1356 1357 1358
    ref = room.players;
    for (j = 0, len = ref.length; j < len; j++) {
      player = ref[j];
nanahira's avatar
nanahira committed
1359
      ygopro.stoc_send(client, 'HS_PLAYER_ENTER', {
nanahira's avatar
nanahira committed
1360 1361
        name: player.name,
        pos: player.pos
nanahira's avatar
nanahira committed
1362
      });
nanahira's avatar
nanahira committed
1363 1364 1365
    }
  };

nanahira's avatar
nanahira committed
1366
  CLIENT_send_reconnect_info = global.CLIENT_send_reconnect_info = function(client, server, room) {
nanahira's avatar
nanahira committed
1367 1368
    client.reconnecting = true;
    ygopro.stoc_send_chat(client, "${reconnecting_to_room}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
    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
1379
        if (client === room.selecting_tp) { // and !client.selected_preduel
nanahira's avatar
nanahira committed
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
          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
1394 1395 1396
    }
  };

nanahira's avatar
nanahira committed
1397
  CLIENT_pre_reconnect = global.CLIENT_pre_reconnect = function(client) {
nanahira's avatar
nanahira committed
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
    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
1411 1412 1413
    }
  };

nanahira's avatar
nanahira committed
1414
  CLIENT_reconnect = global.CLIENT_reconnect = function(client) {
nanahira's avatar
nanahira committed
1415
    var current_old_server, dinfo, room;
nanahira's avatar
nanahira committed
1416 1417 1418 1419 1420 1421 1422
    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
1423
    room = ROOM_all[dinfo.room_id];
nanahira's avatar
nanahira committed
1424 1425 1426 1427 1428 1429
    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
1430
    SERVER_kick(current_old_server);
nanahira's avatar
nanahira committed
1431 1432
    client.established = true;
    client.pre_establish_buffers = [];
nanahira's avatar
nanahira committed
1433 1434 1435
    if (room.random_type || room.arena) {
      room.last_active_time = moment();
    }
nanahira's avatar
nanahira committed
1436 1437
    CLIENT_import_data(client, dinfo.old_client, room);
    CLIENT_send_reconnect_info(client, client.server, room);
nanahira's avatar
nanahira committed
1438 1439
    //console.log("#{client.name} ${reconnect_to_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${reconnect_to_game}`);
nanahira's avatar
nanahira committed
1440 1441 1442
    CLIENT_reconnect_unregister(client, true);
  };

nanahira's avatar
nanahira committed
1443
  CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
    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
1462
    SERVER_kick(current_old_server);
nanahira's avatar
nanahira committed
1463 1464 1465 1466 1467 1468 1469
    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
1470 1471
    //console.log("#{client.name} ${reconnect_to_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${reconnect_to_game}`);
nanahira's avatar
nanahira committed
1472 1473 1474
    CLIENT_reconnect_unregister(client, true);
  };

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

nanahira's avatar
nanahira committed
1485
  CLIENT_heartbeat_register = global.CLIENT_heartbeat_register = function(client, send) {
1486
    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
1487 1488 1489 1490 1491 1492 1493 1494
      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
1495 1496 1497 1498 1499
        player: 0,
        left_time: 0
      });
      ygopro.stoc_send(client, "TIME_LIMIT", {
        player: 1,
nanahira's avatar
nanahira committed
1500 1501 1502 1503 1504 1505 1506 1507
        left_time: 0
      });
    }
    client.heartbeat_timeout = setTimeout(function() {
      CLIENT_heartbeat_unregister(client);
      if (!(client.closed || client.heartbeat_responsed)) {
        client.destroy();
      }
1508
    }, settings.modules.heartbeat_detection.wait_time);
nanahira's avatar
nanahira committed
1509
    //log.info(1, client.name)
nanahira's avatar
nanahira committed
1510 1511 1512
    return true;
  };

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

nanahira's avatar
nanahira committed
1517
  CLIENT_send_replays = global.CLIENT_send_replays = function(client, room) {
nanahira's avatar
nanahira committed
1518
    var buffer, i, j, len, ref;
1519
    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
1520 1521
      return false;
    }
nanahira's avatar
nanahira committed
1522
    client.replays_sent = true;
nanahira's avatar
nanahira committed
1523
    i = 0;
nanahira's avatar
nanahira committed
1524 1525 1526
    ref = room.replays;
    for (j = 0, len = ref.length; j < len; j++) {
      buffer = ref[j];
nanahira's avatar
nanahira committed
1527
      ++i;
nanahira's avatar
fix  
nanahira committed
1528 1529 1530 1531
      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
1532 1533 1534 1535
    }
    return true;
  };

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

nanahira's avatar
nanahira committed
1549
  getSeedTimet = global.getSeedTimet = function(count) {
nanahira's avatar
nanahira committed
1550
    var curTime, i, j, offset, ref, ret;
nanahira's avatar
nanahira committed
1551
    ret = [];
nanahira's avatar
nanahira committed
1552
    for (i = j = 0, ref = count; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
nanahira's avatar
nanahira committed
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572
      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
1573 1574
  Room = class Room {
    constructor(name, hostinfo) {
nanahira's avatar
fixes  
nanahira committed
1575
      var death_time, draw_count, duel_rule, lflist, param, rule, start_hand, start_lp, time_limit;
mercury233's avatar
mercury233 committed
1576 1577
      this.hostinfo = hostinfo;
      this.name = name;
nanahira's avatar
nanahira committed
1578
      //@alive = true
mercury233's avatar
mercury233 committed
1579 1580 1581
      this.players = [];
      this.player_datas = [];
      this.status = 'starting';
nanahira's avatar
nanahira committed
1582
      //@started = false
mercury233's avatar
mercury233 committed
1583 1584 1585
      this.established = false;
      this.watcher_buffers = [];
      this.recorder_buffers = [];
1586
      this.cloud_replay_id = Math.floor(Math.random() * 100000000);
mercury233's avatar
mercury233 committed
1587 1588 1589
      this.watchers = [];
      this.random_type = '';
      this.welcome = '';
mercury233's avatar
mercury233 committed
1590
      this.scores = {};
nanahira's avatar
nanahira committed
1591
      this.decks = {};
1592
      this.duel_count = 0;
nanahira's avatar
nanahira committed
1593
      this.death = 0;
nanahira's avatar
nanahira committed
1594
      this.turn = 0;
nanahira's avatar
nanahira committed
1595
      this.duel_stage = ygopro.constants.DUEL_STAGE.BEGIN;
nanahira's avatar
nanahira committed
1596
      this.replays = [];
nanahira's avatar
nanahira committed
1597
      this.first_list = [];
mercury233's avatar
mercury233 committed
1598
      ROOM_all.push(this);
mercury233's avatar
fix  
mercury233 committed
1599
      this.hostinfo || (this.hostinfo = JSON.parse(JSON.stringify(settings.hostinfo)));
nanahira's avatar
nanahira committed
1600
      delete this.hostinfo.comment;
mercury233's avatar
mercury233 committed
1601
      if (lflists.length) {
1602
        if (this.hostinfo.rule === 1 && this.hostinfo.lflist === 0) {
mercury233's avatar
mercury233 committed
1603
          this.hostinfo.lflist = _.findIndex(lflists, function(list) {
1604 1605 1606 1607 1608 1609
            return list.tcg;
          });
        }
      } else {
        this.hostinfo.lflist = -1;
      }
mercury233's avatar
mercury233 committed
1610 1611 1612 1613 1614
      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
1615 1616 1617
      } else if (name.slice(0, 3) === 'AI#') {
        this.hostinfo.rule = 2;
        this.hostinfo.lflist = -1;
mercury233's avatar
mercury233 committed
1618
        this.hostinfo.time_limit = 999;
mercury233's avatar
mercury233 committed
1619 1620 1621
      } 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]);
1622
        this.hostinfo.duel_rule = (param[3] === 'T' ? 3 : 4);
mercury233's avatar
mercury233 committed
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
        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
1639
          this.hostinfo.lflist = _.findIndex(lflists, function(list) {
mercury233's avatar
mercury233 committed
1640
            return list.tcg;
mercury233's avatar
mercury233 committed
1641 1642 1643 1644
          });
        }
        if (rule.match(/(^|,|,)(OCGONLY|OO)(,|,|$)/)) {
          this.hostinfo.rule = 0;
1645
          this.hostinfo.lflist = 0;
mercury233's avatar
mercury233 committed
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 1703 1704 1705
        }
        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
1706
        if (rule.match(/(^|,|,)(IGPRIORITY|PR)(,|,|$)/)) { // deprecated
nanahira's avatar
nanahira committed
1707
          this.hostinfo.duel_rule = 4;
1708 1709 1710
        }
        if ((param = rule.match(/(^|,|,)(DUELRULE|MR)(\d+)(,|,|$)/))) {
          duel_rule = parseInt(param[3]);
mercury233's avatar
mercury233 committed
1711
          if (duel_rule && duel_rule > 0 && duel_rule <= 5) {
1712 1713
            this.hostinfo.duel_rule = duel_rule;
          }
mercury233's avatar
mercury233 committed
1714
        }
nanahira's avatar
nanahira committed
1715
        if (rule.match(/(^|,|,)(NOWATCH|NW)(,|,|$)/)) {
nanahira's avatar
nanahira committed
1716
          this.hostinfo.no_watch = true;
nanahira's avatar
nanahira committed
1717
        }
nanahira's avatar
nanahira committed
1718 1719 1720 1721 1722 1723 1724 1725
        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
1726
        if (settings.modules.tournament_mode.enable_recover && (param = rule.match(/(^|,|,)(RC|RECOVER)(\d*)T(\d*)(,|,|$)/))) {
nanahira's avatar
nanahira committed
1727 1728 1729
          this.recovered = true;
          this.recovering = true;
          this.recover_from_turn = parseInt(param[4]);
nanahira's avatar
fixes  
nanahira committed
1730
          this.recover_duel_log_id = parseInt(param[3]);
nanahira's avatar
nanahira committed
1731 1732 1733
          this.recover_buffers = [[], [], [], []];
          this.welcome = "${recover_hint}";
        }
mercury233's avatar
mercury233 committed
1734
      }
nanahira's avatar
nanahira committed
1735
      this.hostinfo.replay_mode = 0; // 0x1: Save the replays in file. 0x2: Block the replays to observers.
1736 1737 1738
      if (settings.modules.tournament_mode.enabled) {
        this.hostinfo.replay_mode |= 0x1;
      }
1739
      if ((settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player) || (this.hostinfo.mode === 1 && settings.modules.replay_delay)) {
1740 1741
        this.hostinfo.replay_mode |= 0x2;
      }
nanahira's avatar
fixes  
nanahira committed
1742 1743 1744 1745 1746 1747
      if (!this.recovered) {
        this.spawn();
      }
    }

    spawn(firstSeed) {
nanahira's avatar
nanahira committed
1748
      var i, j, l, param, seeds;
1749
      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
1750 1751
      if (firstSeed) {
        param.push(firstSeed);
nanahira's avatar
nanahira committed
1752
        seeds = getSeedTimet(2);
nanahira's avatar
nanahira committed
1753
        for (i = j = 0; j < 2; i = ++j) {
nanahira's avatar
nanahira committed
1754
          param.push(seeds[i]);
nanahira's avatar
nanahira committed
1755 1756
        }
      } else {
nanahira's avatar
nanahira committed
1757
        seeds = getSeedTimet(3);
nanahira's avatar
nanahira committed
1758
        for (i = l = 0; l < 3; i = ++l) {
nanahira's avatar
nanahira committed
1759
          param.push(seeds[i]);
nanahira's avatar
nanahira committed
1760
        }
nanahira's avatar
nanahira committed
1761
      }
mercury233's avatar
mercury233 committed
1762 1763
      try {
        this.process = spawn('./ygopro', param, {
mercury233's avatar
mercury233 committed
1764
          cwd: 'ygopro'
mercury233's avatar
mercury233 committed
1765
        });
nanahira's avatar
nanahira committed
1766
        this.process_pid = this.process.pid;
nanahira's avatar
nanahira committed
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778
        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
1779
        this.process.stdout.setEncoding('utf8');
nanahira's avatar
nanahira committed
1780 1781 1782 1783 1784 1785 1786 1787
        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
1788 1789 1790 1791
              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
1792 1793 1794 1795
                player.server.write(buffer);
              }
              player.established = true;
              player.pre_establish_buffers = [];
mercury233's avatar
mercury233 committed
1796
            });
nanahira's avatar
nanahira committed
1797 1798 1799 1800 1801 1802 1803
          });
          if (this.windbot) {
            setTimeout(() => {
              return this.add_windbot(this.windbot);
            }, 200);
          }
        });
nanahira's avatar
fixes  
nanahira committed
1804
        return this.process.stderr.on('data', (data) => {
nanahira's avatar
nanahira committed
1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815
          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
1816
      } catch (error1) {
nanahira's avatar
fixes  
nanahira committed
1817
        return this.error = "${create_room_failed}";
mercury233's avatar
mercury233 committed
1818 1819 1820
      }
    }

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

nanahira's avatar
fixes  
nanahira committed
1974
    async initialize_recover() {
nanahira's avatar
nanahira committed
1975
      var e;
nanahira's avatar
fixes  
nanahira committed
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992
      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
1993
    get_playing_player() {
mercury233's avatar
mercury233 committed
1994 1995 1996 1997 1998 1999 2000 2001
      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
2002
    }
mercury233's avatar
mercury233 committed
2003

nanahira's avatar
nanahira committed
2004
    get_host() {
mercury233's avatar
mercury233 committed
2005 2006 2007 2008 2009 2010 2011 2012
      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
2013
    }
mercury233's avatar
mercury233 committed
2014

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

nanahira's avatar
nanahira committed
2031
    get_challonge_score() {
nanahira's avatar
nanahira committed
2032
      var challonge_duel_log;
nanahira's avatar
nanahira committed
2033
      if (!settings.modules.challonge.enabled || this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || this.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
2034 2035 2036
        return null;
      }
      challonge_duel_log = {};
2037
      if (this.scores[this.dueling_players[0].name_vpass] > this.scores[this.dueling_players[1].name_vpass]) {
nanahira's avatar
nanahira committed
2038
        challonge_duel_log.winnerId = this.dueling_players[0].challonge_info.id;
2039
      } else if (this.scores[this.dueling_players[0].name_vpass] < this.scores[this.dueling_players[1].name_vpass]) {
nanahira's avatar
nanahira committed
2040 2041 2042 2043 2044 2045
        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) {
2046
          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
2047
        } else if (this.dueling_players[1].challonge_info.id === this.challonge_info.player1Id && this.dueling_players[0].challonge_info.id === this.challonge_info.player2Id) {
2048
          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
2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062
        } 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
2063
    }
nanahira's avatar
nanahira committed
2064

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

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

nanahira's avatar
nanahira committed
2094
    add_windbot(botdata) {
mercury233's avatar
mercury233 committed
2095 2096
      this.windbot = botdata;
      request({
nanahira's avatar
nanahira committed
2097 2098 2099 2100 2101 2102 2103 2104
        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
2105

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

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

nanahira's avatar
nanahira committed
2215
    start_death() {
nanahira's avatar
nanahira committed
2216
      var oppo_pos, win_pos;
nanahira's avatar
nanahira committed
2217
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || this.death) {
nanahira's avatar
nanahira committed
2218 2219 2220
        return false;
      }
      oppo_pos = this.hostinfo.mode === 2 ? 2 : 1;
nanahira's avatar
nanahira committed
2221
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
nanahira's avatar
nanahira committed
2222 2223 2224 2225 2226 2227 2228
        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
2229
            ygopro.stoc_send_chat_to_room(this, "${death_start}", ygopro.constants.COLORS.BABYBLUE); // Extra duel started in siding
nanahira's avatar
nanahira committed
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 2268 2269 2270
        }
      } 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
2271
    }
nanahira's avatar
nanahira committed
2272

nanahira's avatar
nanahira committed
2273
    cancel_death() {
nanahira's avatar
nanahira committed
2274
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || !this.death) {
nanahira's avatar
nanahira committed
2275 2276 2277 2278 2279
        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
2280
    }
mercury233's avatar
mercury233 committed
2281

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

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

nanahira's avatar
nanahira committed
2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346
    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
2347
  };
mercury233's avatar
mercury233 committed
2348

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

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

Yuzurisa's avatar
Yuzurisa committed
2580 2581 2582 2583 2584
  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
2585
    parsed_deck_name = deck_name.match(/^([^\+ \uff0b]+)[\+ \uff0b](.+?)(\.ydk){0,2}$/);
Yuzurisa's avatar
Yuzurisa committed
2586 2587 2588
    return parsed_deck_name && (player_name === parsed_deck_name[1] || player_name === parsed_deck_name[2]);
  };

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

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

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

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

nanahira's avatar
nanahira committed
3297
  ygopro.stoc_follow('GAME_MSG', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3298
    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;
3299
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3300
    if (!(room && !client.reconnecting)) {
3301 3302
      return;
    }
神楽坂玲奈's avatar
神楽坂玲奈 committed
3303
    msg = buffer.readInt8(0);
nanahira's avatar
nanahira committed
3304 3305 3306 3307 3308
    //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
3309 3310 3311 3312 3313 3314 3315 3316 3317 3318
    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
3319
          CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
3320
          CLIENT_kick(client);
nanahira's avatar
nanahira committed
3321 3322 3323 3324 3325 3326 3327 3328
          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
3329 3330 3331
          if (client.last_hint_msg) {
            ygopro.stoc_send(client, 'GAME_MSG', client.last_hint_msg);
          }
nanahira's avatar
nanahira committed
3332 3333 3334 3335 3336
          ygopro.stoc_send(client, 'GAME_MSG', client.last_game_msg);
          return true;
        }
      } else {
        client.last_game_msg = buffer;
nanahira's avatar
nanahira committed
3337
        client.last_game_msg_title = ygopro.constants.MSG[msg];
nanahira's avatar
nanahira committed
3338
      }
nanahira's avatar
nanahira committed
3339
    // log.info(client.name, client.last_game_msg_title)
nanahira's avatar
nanahira committed
3340 3341 3342
    } 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
3343
    }
nanahira's avatar
nanahira committed
3344 3345
    // 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
3346 3347 3348 3349 3350 3351 3352 3353 3354 3355
      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();
      }
3356
    }
nanahira's avatar
nanahira committed
3357 3358 3359
    //log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}")

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

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

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

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

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

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

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

nanahira's avatar
nanahira committed
3851
  ygopro.stoc_follow('FIELD_FINISH', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3852 3853
    var room;
    room = ROOM_all[client.rid];
3854
    if (!(room && settings.modules.reconnect.enabled)) {
nanahira's avatar
nanahira committed
3855 3856
      return true;
    }
3857
    client.reconnecting = false;
nanahira's avatar
nanahira committed
3858
    if (client.time_confirm_required) { // client did not send TIME_CONFIRM
3859
      client.waiting_for_last = true;
nanahira's avatar
nanahira committed
3860
    } else if (client.last_game_msg && client.last_game_msg_title !== 'WAITING') { // client sent TIME_CONFIRM
3861 3862 3863 3864
      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
3865 3866 3867 3868
    }
    return true;
  });

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

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

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

nanahira's avatar
nanahira committed
3948
  //tip
nanahira's avatar
nanahira committed
3949
  ygopro.stoc_send_random_tip = async function(client) {
mercury233's avatar
mercury233 committed
3950 3951
    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
3952
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3953 3954
  };

nanahira's avatar
nanahira committed
3955
  ygopro.stoc_send_random_tip_to_room = async function(room) {
mercury233's avatar
mercury233 committed
3956 3957
    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)]);
3958 3959 3960
    }
  };

nanahira's avatar
nanahira committed
3961 3962 3963 3964 3965 3966
  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
3967
      if (_.isString(body)) {
nanahira's avatar
nanahira committed
3968 3969
        log.warn(`${name} bad json`, body);
        return false;
mercury233's avatar
mercury233 committed
3970
      }
nanahira's avatar
nanahira committed
3971 3972 3973
      if (!body) {
        log.warn(`${name} empty`, body);
        return false;
nanahira's avatar
nanahira committed
3974
      }
nanahira's avatar
nanahira committed
3975 3976 3977 3978 3979 3980 3981 3982
      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
3983 3984
  };

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

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

nanahira's avatar
nanahira committed
4097
  ygopro.ctos_follow('SURRENDER', true, async function(buffer, info, client, server, datas) {
4098 4099 4100 4101 4102
    var room;
    room = ROOM_all[client.rid];
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
4103
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || room.hostinfo.mode === 2) {
4104 4105
      return true;
    }
4106
    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)) {
4107 4108 4109 4110 4111 4112
      ygopro.stoc_send_chat(client, "${surrender_denied}", ygopro.constants.COLORS.BABYBLUE);
      return true;
    }
    return false;
  });

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

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

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

nanahira's avatar
nanahira committed
4517
  ygopro.stoc_follow('TIME_LIMIT', true, async function(buffer, info, client, server, datas) {
4518
    var check, cur_players, room;
nanahira's avatar
nanahira committed
4519
    room = ROOM_all[client.rid];
nanahira's avatar
js  
nanahira committed
4520 4521 4522
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
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 4549 4550
    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
4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567
    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
4568
    if (check) {
4569
      CLIENT_heartbeat_register(client, false);
nanahira's avatar
nanahira committed
4570
    }
nanahira's avatar
js  
nanahira committed
4571
    return false;
nanahira's avatar
nanahira committed
4572 4573
  });

nanahira's avatar
nanahira committed
4574
  ygopro.ctos_follow('TIME_CONFIRM', false, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4575 4576
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4577
    if (!room) {
nanahira's avatar
nanahira committed
4578 4579
      return;
    }
nanahira's avatar
nanahira committed
4580 4581 4582
    if (room.recovered) {
      room.recovered = false;
    }
nanahira's avatar
nanahira committed
4583
    if (settings.modules.reconnect.enabled) {
4584 4585 4586 4587 4588 4589 4590 4591 4592
      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
4593 4594
      client.time_confirm_required = false;
    }
4595 4596 4597 4598
    if (settings.modules.heartbeat_detection.enabled) {
      client.heartbeat_protected = false;
      client.heartbeat_responsed = true;
      CLIENT_heartbeat_unregister(client);
nanahira's avatar
typo  
nanahira committed
4599
    }
nanahira's avatar
nanahira committed
4600 4601
  });

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

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

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

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

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

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

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

nanahira's avatar
nanahira committed
4789
  ygopro.stoc_follow('REPLAY', true, async function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4790
    var i, j, l, len, len1, player, playerInfos, ref, ref1, replay_filename, room;
4791 4792
    room = ROOM_all[client.rid];
    if (!room) {
4793
      return settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player || settings.modules.replay_delay;
4794
    }
nanahira's avatar
nanahira committed
4795
    if (!room.replays[room.duel_count - 1]) {
nanahira's avatar
nanahira committed
4796
      // console.log("Replay saved: ", room.duel_count - 1, client.pos)
4797
      room.replays[room.duel_count - 1] = buffer;
nanahira's avatar
nanahira committed
4798
    }
nanahira's avatar
fixes  
nanahira committed
4799
    if (settings.modules.mysql.enabled) {
nanahira's avatar
nanahira committed
4800
      if (client.pos === 0) {
nanahira's avatar
fixes  
nanahira committed
4801
        replay_filename = moment().format("YYYY-MM-DD HH-mm-ss");
nanahira's avatar
fix  
nanahira committed
4802
        if (room.hostinfo.mode !== 2) {
nanahira's avatar
nanahira committed
4803 4804 4805
          ref = room.dueling_players;
          for (i = j = 0, len = ref.length; j < len; i = ++j) {
            player = ref[i];
nanahira's avatar
nanahira committed
4806 4807 4808
            replay_filename = replay_filename + (i > 0 ? " VS " : " ") + player.name;
          }
        } else {
nanahira's avatar
nanahira committed
4809 4810 4811
          ref1 = room.dueling_players;
          for (i = l = 0, len1 = ref1.length; l < len1; i = ++l) {
            player = ref1[i];
nanahira's avatar
nanahira committed
4812 4813
            replay_filename = replay_filename + (i > 0 ? (i === 2 ? " VS " : " & ") : " ") + player.name;
          }
mercury233's avatar
mercury233 committed
4814 4815
        }
        replay_filename = replay_filename.replace(/[\/\\\?\*]/g, '_') + ".yrp";
nanahira's avatar
fixes  
nanahira committed
4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833
        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
4834 4835 4836 4837 4838
        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
4839
        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
4840
      }
nanahira's avatar
fix  
nanahira committed
4841
      if (settings.modules.cloud_replay.enabled && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.replay_safe) {
nanahira's avatar
nanahira committed
4842
        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
4843
      }
nanahira's avatar
js  
nanahira committed
4844
      return settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player || settings.modules.replay_delay && room.hostinfo.mode === 1;
4845
    } else {
nanahira's avatar
nanahira committed
4846
      return settings.modules.replay_delay && room.hostinfo.mode === 1;
4847 4848 4849
    }
  });

nanahira's avatar
nanahira committed
4850
  // spawn windbot
nanahira's avatar
nanahira committed
4851 4852
  windbot_looplimit = 0;

nanahira's avatar
nanahira committed
4853
  windbot_process = global.windbot_process = null;
nanahira's avatar
nanahira committed
4854

nanahira's avatar
nanahira committed
4855
  spawn_windbot = global.spawn_windbot = function() {
nanahira's avatar
nanahira committed
4856
    var windbot_bin, windbot_parameters;
mercury233's avatar
mercury233 committed
4857 4858 4859 4860 4861 4862 4863 4864 4865 4866
    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
4867 4868 4869 4870
      cwd: 'windbot'
    });
    windbot_process.on('error', function(err) {
      log.warn('WindBot ERROR', err);
nanahira's avatar
nanahira committed
4871
      if (windbot_looplimit < 1000 && !global.rebooted) {
nanahira's avatar
nanahira committed
4872 4873 4874
        windbot_looplimit++;
        spawn_windbot();
      }
mercury233's avatar
mercury233 committed
4875 4876 4877
    });
    windbot_process.on('exit', function(code) {
      log.warn('WindBot EXIT', code);
nanahira's avatar
nanahira committed
4878
      if (windbot_looplimit < 1000 && !global.rebooted) {
nanahira's avatar
nanahira committed
4879 4880 4881
        windbot_looplimit++;
        spawn_windbot();
      }
mercury233's avatar
mercury233 committed
4882 4883 4884 4885 4886
    });
    windbot_process.stdout.setEncoding('utf8');
    windbot_process.stdout.on('data', function(data) {
      log.info('WindBot:', data);
    });
mercury233's avatar
fix  
mercury233 committed
4887
    windbot_process.stderr.setEncoding('utf8');
nanahira's avatar
nanahira committed
4888
    windbot_process.stderr.on('data', function(data) {
mercury233's avatar
mercury233 committed
4889 4890
      log.warn('WindBot Error:', data);
    });
nanahira's avatar
nanahira committed
4891 4892
  };

nanahira's avatar
nanahira committed
4893
  global.rebooted = false;
nanahira's avatar
nanahira committed
4894

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

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

nanahira's avatar
nanahira committed
5261
  init();
nanahira's avatar
nanahira committed
5262

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