ygopro-server.js 205 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, Cloud_replay_ids, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_banned, ROOM_players_oppentlist, ROOM_players_scores, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, auth, badwords, ban_user, bunyan, challonge, challonge_cache, challonge_module_name, challonge_queue_callbacks, chat_color, config, cppversion, crypto, date, deck_name_match, default_config, default_data, dialogues, disconnect_list, dns, duel_log, e, exec, execFile, fs, geoip, get_callback, get_memory_usage, http, http_server, https, https_server, import_datas, imported, is_requesting, j, l, len, len1, len2, lflists, list, loadJSON, load_dialogues, load_tips, log, long_resolve_cards, m, memory_usage, merge, moment, net, oldbadwords, oldconfig, olddialogues, oldduellog, oldtips, options, os, path, pgClient, pg_client, pg_query, plugin_filename, plugin_list, plugin_path, real_windbot_server_ip, redis, redisdb, ref, ref1, refresh_challonge_cache, release_disconnect, report_to_big_brother, request, requestListener, roomlist, setting_change, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, url, users_cache, util, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib;
神楽坂玲奈'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');

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

nanahira's avatar
nanahira committed
41
  log = global.log = bunyan.createLogger({
mercury233's avatar
mercury233 committed
42 43 44
    name: "mycard"
  });

nanahira's avatar
nanahira committed
45
  moment = global.moment = require('moment');
46

mercury233's avatar
mercury233 committed
47
  moment.updateLocale('zh-cn', {
mercury233's avatar
mercury233 committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    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
65
  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
66

mercury233's avatar
mercury233 committed
67 68
  merge = require('deepmerge');

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

nanahira's avatar
nanahira committed
71 72
  util = require("util");

nanahira's avatar
nanahira committed
73 74 75 76
  //heapdump = require 'heapdump'

  // 配置
  // 导入旧配置
mercury233's avatar
mercury233 committed
77 78 79 80
  if (!fs.existsSync('./config')) {
    fs.mkdirSync('./config');
  }

mercury233's avatar
mercury233 committed
81
  try {
mercury233's avatar
mercury233 committed
82
    oldconfig = loadJSON('./config.user.json');
mercury233's avatar
mercury233 committed
83 84 85 86 87 88 89 90 91 92 93 94 95 96
    if (oldconfig.tips) {
      oldtips = {};
      oldtips.file = './config/tips.json';
      oldtips.tips = oldconfig.tips;
      fs.writeFileSync(oldtips.file, JSON.stringify(oldtips, null, 2));
      delete oldconfig.tips;
    }
    if (oldconfig.dialogues) {
      olddialogues = {};
      olddialogues.file = './config/dialogues.json';
      olddialogues.dialogues = oldconfig.dialogues;
      fs.writeFileSync(olddialogues.file, JSON.stringify(olddialogues, null, 2));
      delete oldconfig.dialogues;
    }
mercury233's avatar
fix  
mercury233 committed
97 98 99 100 101 102 103 104
    if (oldconfig.modules) {
      if (oldconfig.modules.tournament_mode && oldconfig.modules.tournament_mode.duel_log) {
        oldduellog = {};
        oldduellog.file = './config/duel_log.json';
        oldduellog.duel_log = oldconfig.modules.tournament_mode.duel_log;
        fs.writeFileSync(oldduellog.file, JSON.stringify(oldduellog, null, 2));
        delete oldconfig.oldduellog;
      }
105
    }
mercury233's avatar
mercury233 committed
106
    oldbadwords = {};
mercury233's avatar
fix  
mercury233 committed
107 108 109 110 111 112 113 114 115 116 117 118 119
    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;
      }
mercury233's avatar
mercury233 committed
120 121 122 123 124 125 126 127 128 129
    }
    if (!_.isEmpty(oldbadwords)) {
      oldbadwords.file = './config/badwords.json';
      fs.writeFileSync(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)) {
nanahira's avatar
nanahira committed
130
      // log.info oldconfig
mercury233's avatar
mercury233 committed
131 132 133 134 135 136
      fs.writeFileSync('./config/config.json', JSON.stringify(oldconfig, null, 2));
      log.info('imported old config from config.user.json');
    }
    fs.renameSync('./config.user.json', './config.user.bak');
  } catch (error1) {
    e = error1;
mercury233's avatar
mercury233 committed
137
    if (e.code !== 'ENOENT') {
mercury233's avatar
mercury233 committed
138 139 140
      log.info(e);
    }
  }
mercury233's avatar
mercury233 committed
141

nanahira's avatar
nanahira committed
142 143 144 145 146 147 148 149 150
  setting_save = global.setting_save = function(settings, callback) {
    if (!callback) {
      callback = function(err) {
        if (err) {
          return log.warn("setting save fail", err.toString());
        }
      };
    }
    fs.writeFile(settings.file, JSON.stringify(settings, null, 2), callback);
mercury233's avatar
mercury233 committed
151
  };
mercury233's avatar
mercury233 committed
152

nanahira's avatar
nanahira committed
153
  setting_change = global.setting_change = function(settings, path, val, callback) {
mercury233's avatar
mercury233 committed
154
    var key, target;
mercury233's avatar
mercury233 committed
155
    if (_.isString(val)) {
nanahira's avatar
nanahira committed
156
      // path should be like "modules:welcome"
mercury233's avatar
mercury233 committed
157 158
      log.info("setting changed", path, val);
    }
mercury233's avatar
mercury233 committed
159 160 161 162 163 164 165 166 167 168 169 170
    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
nanahira committed
171
    setting_save(settings, callback);
mercury233's avatar
mercury233 committed
172
  };
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
173

nanahira's avatar
nanahira committed
174
  // 读取配置
mercury233's avatar
mercury233 committed
175
  default_config = loadJSON('./data/default_config.json');
mercury233's avatar
mercury233 committed
176 177

  try {
mercury233's avatar
mercury233 committed
178
    config = loadJSON('./config/config.json');
mercury233's avatar
mercury233 committed
179 180 181 182 183 184 185 186 187 188
  } catch (error1) {
    config = {};
  }

  settings = global.settings = merge(default_config, config, {
    arrayMerge: function(destination, source) {
      return source;
    }
  });

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

nanahira's avatar
nanahira committed
191
  //import old configs
nanahira's avatar
nanahira committed
192 193
  imported = false;

nanahira's avatar
nanahira committed
194
  //reset http.quick_death_rule from true to 1
nanahira's avatar
nanahira committed
195 196
  if (settings.modules.http.quick_death_rule === true) {
    settings.modules.http.quick_death_rule = 1;
nanahira's avatar
nanahira committed
197 198 199
    imported = true;
  }

nanahira's avatar
nanahira committed
200
  //import the old redis port
nanahira's avatar
nanahira committed
201 202 203 204 205 206
  if (settings.modules.cloud_replay.redis_port) {
    settings.modules.cloud_replay.redis.port = settings.modules.cloud_replay.redis_port;
    delete settings.modules.cloud_replay.redis_port;
    imported = true;
  }

nanahira's avatar
nanahira committed
207
  //import the old passwords to new admin user system
nanahira's avatar
nanahira committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  if (settings.modules.http.password) {
    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) {
    auth.add_user("tournament", settings.modules.tournament_mode.password, true, {
      "duel_log": true,
      "download_replay": true,
      "clear_duel_log": true,
nanahira's avatar
fix  
nanahira committed
227
      "deck_dashboard_read": true,
nanahira's avatar
nanahira committed
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
      "deck_dashboard_write": true
    });
    delete settings.modules.tournament_mode.password;
    imported = true;
  }

  if (settings.modules.pre_util.password) {
    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) {
    auth.add_user("update", settings.modules.update_util.password, true, {
      "update_dashboard": true
    });
    delete settings.modules.update_util.password;
    imported = true;
  }

nanahira's avatar
nanahira committed
250
  //import the old enable_priority hostinfo
251 252 253 254
  if (settings.hostinfo.enable_priority || settings.hostinfo.enable_priority === false) {
    if (settings.hostinfo.enable_priority) {
      settings.hostinfo.duel_rule = 3;
    } else {
nanahira's avatar
nanahira committed
255
      settings.hostinfo.duel_rule = 5;
256 257 258 259 260
    }
    delete settings.hostinfo.enable_priority;
    imported = true;
  }

nanahira's avatar
nanahira committed
261
  //import the old Challonge api key option
262 263 264 265 266 267
  if (settings.modules.challonge.api_key) {
    settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key;
    delete settings.modules.challonge.api_key;
    imported = true;
  }

268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
  //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;
  }

nanahira's avatar
nanahira committed
289
  //finish
nanahira's avatar
nanahira committed
290
  if (imported) {
nanahira's avatar
nanahira committed
291 292 293
    setting_save(settings);
  }

nanahira's avatar
nanahira committed
294
  // 读取数据
mercury233's avatar
mercury233 committed
295
  default_data = loadJSON('./data/default_data.json');
mercury233's avatar
mercury233 committed
296 297

  try {
nanahira's avatar
nanahira committed
298
    tips = global.tips = loadJSON('./config/tips.json');
mercury233's avatar
mercury233 committed
299
  } catch (error1) {
nanahira's avatar
nanahira committed
300
    tips = global.tips = default_data.tips;
mercury233's avatar
mercury233 committed
301 302 303 304
    setting_save(tips);
  }

  try {
nanahira's avatar
nanahira committed
305
    dialogues = global.dialogues = loadJSON('./config/dialogues.json');
mercury233's avatar
mercury233 committed
306
  } catch (error1) {
nanahira's avatar
nanahira committed
307
    dialogues = global.dialogues = default_data.dialogues;
mercury233's avatar
mercury233 committed
308 309 310 311
    setting_save(dialogues);
  }

  try {
nanahira's avatar
nanahira committed
312
    badwords = global.badwords = loadJSON('./config/badwords.json');
mercury233's avatar
mercury233 committed
313
  } catch (error1) {
nanahira's avatar
nanahira committed
314
    badwords = global.badwords = default_data.badwords;
mercury233's avatar
mercury233 committed
315 316 317
    setting_save(badwords);
  }

318
  try {
nanahira's avatar
nanahira committed
319
    duel_log = global.duel_log = loadJSON('./config/duel_log.json');
320
  } catch (error1) {
nanahira's avatar
nanahira committed
321
    duel_log = global.duel_log = default_data.duel_log;
322 323 324
    setting_save(duel_log);
  }

nanahira's avatar
nanahira committed
325
  try {
nanahira's avatar
nanahira committed
326
    chat_color = global.chat_color = loadJSON('./config/chat_color.json');
nanahira's avatar
nanahira committed
327
  } catch (error1) {
nanahira's avatar
nanahira committed
328
    chat_color = global.chat_color = default_data.chat_color;
nanahira's avatar
nanahira committed
329 330 331
    setting_save(chat_color);
  }

332
  try {
mercury233's avatar
mercury233 committed
333
    cppversion = parseInt(fs.readFileSync('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16');
mercury233's avatar
mercury233 committed
334
    setting_change(settings, "version", cppversion);
mercury233's avatar
fix  
mercury233 committed
335
    log.info("ygopro version 0x" + settings.version.toString(16), "(from source code)");
336
  } catch (error1) {
nanahira's avatar
nanahira committed
337
    //settings.version = settings.version_default
mercury233's avatar
fix  
mercury233 committed
338
    log.info("ygopro version 0x" + settings.version.toString(16), "(from config)");
339
  }
340

nanahira's avatar
nanahira committed
341
  // load the lflist of current date
nanahira's avatar
nanahira committed
342
  lflists = global.lflists = [];
nanahira's avatar
nanahira committed
343 344 345

  try {
    ref = fs.readFileSync('ygopro/expansions/lflist.conf', 'utf8').match(/!.*/g);
nanahira's avatar
nanahira committed
346
    // expansions/lflist
mercury233's avatar
mercury233 committed
347 348
    for (j = 0, len = ref.length; j < len; j++) {
      list = ref[j];
mercury233's avatar
mercury233 committed
349 350 351 352
      date = list.match(/!([\d\.]+)/);
      if (!date) {
        continue;
      }
nanahira's avatar
nanahira committed
353
      lflists.push({
神楽坂玲奈's avatar
神楽坂玲奈 committed
354
        date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"),
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
355
        tcg: list.indexOf('TCG') !== -1
神楽坂玲奈's avatar
神楽坂玲奈 committed
356 357
      });
    }
nanahira's avatar
nanahira committed
358 359 360 361 362 363
  } catch (error1) {

  }

  try {
    ref1 = fs.readFileSync('ygopro/lflist.conf', 'utf8').match(/!.*/g);
nanahira's avatar
nanahira committed
364
    // lflist
nanahira's avatar
nanahira committed
365 366
    for (l = 0, len1 = ref1.length; l < len1; l++) {
      list = ref1[l];
nanahira's avatar
nanahira committed
367 368 369 370 371 372 373 374 375 376 377 378
      date = list.match(/!([\d\.]+)/);
      if (!date) {
        continue;
      }
      lflists.push({
        date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"),
        tcg: list.indexOf('TCG') !== -1
      });
    }
  } catch (error1) {

  }
神楽坂玲奈's avatar
神楽坂玲奈 committed
379

mercury233's avatar
mercury233 committed
380
  if (settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
381 382
    redis = require('redis');
    zlib = require('zlib');
nanahira's avatar
nanahira committed
383
    redisdb = global.redisdb = redis.createClient(settings.modules.cloud_replay.redis);
mercury233's avatar
mercury233 committed
384 385 386
    redisdb.on('error', function(err) {
      log.warn(err);
    });
mercury233's avatar
mercury233 committed
387 388
  }

mercury233's avatar
mercury233 committed
389
  if (settings.modules.windbot.enabled) {
nanahira's avatar
nanahira committed
390
    windbots = global.windbots = loadJSON(settings.modules.windbot.botlist).windbots;
nanahira's avatar
fix  
nanahira committed
391
    real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip;
nanahira's avatar
nanahira committed
392 393 394 395
    if (!settings.modules.windbot.server_ip.includes("127.0.0.1")) {
      dns = require('dns');
      dns.lookup(settings.modules.windbot.server_ip, function(err, addr) {
        if (!err) {
nanahira's avatar
fix  
nanahira committed
396
          return real_windbot_server_ip = global.real_windbot_server_ip = addr;
nanahira's avatar
nanahira committed
397 398 399
        }
      });
    }
mercury233's avatar
mercury233 committed
400 401
  }

nanahira's avatar
nanahira committed
402
  if (settings.modules.heartbeat_detection.enabled) {
nanahira's avatar
nanahira committed
403
    long_resolve_cards = global.long_resolve_cards = loadJSON('./data/long_resolve_cards.json');
nanahira's avatar
nanahira committed
404 405
  }

nanahira's avatar
nanahira committed
406 407 408 409
  if (settings.modules.tournament_mode.enable_recover) {
    ReplayParser = global.ReplayParser = require("./Replay.js");
  }

nanahira's avatar
nanahira committed
410
  // 组件
nanahira's avatar
nanahira committed
411
  ygopro = global.ygopro = require('./ygopro.js');
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
412

mercury233's avatar
mercury233 committed
413
  if (settings.modules.http.websocket_roomlist) {
nanahira's avatar
nanahira committed
414
    roomlist = global.roomlist = require('./roomlist.js');
mercury233's avatar
mercury233 committed
415
  }
神楽坂玲奈's avatar
神楽坂玲奈 committed
416

mercury233's avatar
mercury233 committed
417 418 419 420
  if (settings.modules.i18n.auto_pick) {
    geoip = require('geoip-country-lite');
  }

nanahira's avatar
nanahira committed
421
  // cache users of mycard login
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
422 423
  users_cache = {};

mercury233's avatar
mercury233 committed
424
  if (settings.modules.mycard.enabled) {
mercury233's avatar
merge  
mercury233 committed
425
    pgClient = require('pg').Client;
nanahira's avatar
nanahira committed
426
    pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database);
mercury233's avatar
fix  
mercury233 committed
427 428 429
    pg_client.on('error', function(err) {
      log.warn("PostgreSQL ERROR: ", err);
    });
mercury233's avatar
merge  
mercury233 committed
430
    pg_query = pg_client.query('SELECT username, id from users');
mercury233's avatar
fix  
mercury233 committed
431 432 433
    pg_query.on('error', function(err) {
      log.warn("PostgreSQL Query ERROR: ", err);
    });
mercury233's avatar
merge  
mercury233 committed
434
    pg_query.on('row', function(row) {
nanahira's avatar
nanahira committed
435
      //log.info "load user", row.username, row.id
mercury233's avatar
merge  
mercury233 committed
436 437 438 439 440 441 442 443
      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();
nanahira's avatar
nanahira committed
444 445 446 447 448 449 450
    if (settings.modules.arena_mode.enabled && settings.modules.arena_mode.init_post.enabled) {
      request.post({
        url: settings.modules.arena_mode.init_post.url,
        qs: {
          ak: settings.modules.arena_mode.init_post.accesskey,
          arena: settings.modules.arena_mode.mode
        }
nanahira's avatar
nanahira committed
451 452 453 454 455 456
      }, (error, response, body) => {
        if (error) {
          log.warn('ARENA INIT POST ERROR', error);
        } else {
          if (response.statusCode >= 400) {
            log.warn('ARENA INIT POST FAIL', response.statusCode, response.statusMessage, body);
nanahira's avatar
nanahira committed
457
          }
nanahira's avatar
nanahira committed
458 459
        }
      });
nanahira's avatar
nanahira committed
460
    }
mercury233's avatar
merge  
mercury233 committed
461 462
  }

nanahira's avatar
nanahira committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
  ResolveData = (function() {
    //else
    //  log.info 'ARENA INIT POST OK', response.statusCode, response.statusMessage
    class ResolveData {
      constructor(func) {
        this.func = func;
      }

      resolve(err, data) {
        if (this.resolved) {
          return false;
        }
        this.func(err, data);
        return true;
      }

    };

    ResolveData.prototype.resolved = false;

    return ResolveData;

  }).call(this);

nanahira's avatar
nanahira committed
487
  if (settings.modules.challonge.enabled) {
488 489 490 491
    challonge_module_name = 'challonge';
    if (settings.modules.challonge.use_custom_module) {
      challonge_module_name = settings.modules.challonge.use_custom_module;
    }
nanahira's avatar
nanahira committed
492
    challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options);
nanahira's avatar
nanahira committed
493 494 495
    if (settings.modules.challonge.cache_ttl) {
      challonge_cache = [];
    }
nanahira's avatar
nanahira committed
496
    challonge_queue_callbacks = [[], []];
nanahira's avatar
nanahira committed
497
    is_requesting = [null, null];
nanahira's avatar
ref  
nanahira committed
498
    get_callback = function(challonge_type, resolve_data) {
nanahira's avatar
nanahira committed
499
      return (function(err, data) {
nanahira's avatar
ref  
nanahira committed
500
        var cur_resolve_data;
nanahira's avatar
nanahira committed
501
        if (settings.modules.challonge.cache_ttl && !err && data) {
nanahira's avatar
nanahira committed
502 503
          challonge_cache[challonge_type] = data;
        }
nanahira's avatar
nanahira committed
504
        is_requesting[challonge_type] = null;
nanahira's avatar
nanahira committed
505
        resolve_data.resolve(err, data);
nanahira's avatar
nanahira committed
506
        while (challonge_queue_callbacks[challonge_type].length) {
nanahira's avatar
ref  
nanahira committed
507
          cur_resolve_data = challonge_queue_callbacks[challonge_type].splice(0, 1)[0];
nanahira's avatar
nanahira committed
508
          cur_resolve_data.resolve(err, data);
nanahira's avatar
nanahira committed
509 510 511 512
        }
      });
    };
    challonge.participants._index = function(_data) {
nanahira's avatar
ref  
nanahira committed
513
      var err, resolve_data;
nanahira's avatar
nanahira committed
514
      resolve_data = new ResolveData(_data.callback);
nanahira's avatar
nanahira committed
515
      if (settings.modules.challonge.cache_ttl && challonge_cache[0]) {
nanahira's avatar
nanahira committed
516
        resolve_data.resolve(null, challonge_cache[0]);
nanahira's avatar
nanahira committed
517
      } else if (is_requesting[0] && moment() - is_requesting[0] <= 5000) {
nanahira's avatar
ref  
nanahira committed
518
        challonge_queue_callbacks[0].push(resolve_data);
nanahira's avatar
nanahira committed
519
      } else {
nanahira's avatar
ref  
nanahira committed
520
        _data.callback = get_callback(0, resolve_data);
nanahira's avatar
nanahira committed
521
        is_requesting[0] = moment();
nanahira's avatar
js  
nanahira committed
522 523 524 525 526 527
        try {
          challonge.participants.index(_data);
        } catch (error1) {
          err = error1;
          _data.callback(err, null);
        }
nanahira's avatar
nanahira committed
528 529 530
      }
    };
    challonge.matches._index = function(_data) {
nanahira's avatar
ref  
nanahira committed
531
      var err, resolve_data;
nanahira's avatar
nanahira committed
532
      resolve_data = new ResolveData(_data.callback);
nanahira's avatar
nanahira committed
533
      if (settings.modules.challonge.cache_ttl && challonge_cache[1]) {
nanahira's avatar
nanahira committed
534
        resolve_data.resolve(null, challonge_cache[1]);
nanahira's avatar
nanahira committed
535
      } else if (is_requesting[1] && moment() - is_requesting[1] <= 5000) {
nanahira's avatar
ref  
nanahira committed
536
        challonge_queue_callbacks[1].push(resolve_data);
nanahira's avatar
nanahira committed
537
      } else {
nanahira's avatar
ref  
nanahira committed
538
        _data.callback = get_callback(1, resolve_data);
nanahira's avatar
nanahira committed
539
        is_requesting[1] = moment();
nanahira's avatar
js  
nanahira committed
540 541 542 543 544 545
        try {
          challonge.matches.index(_data);
        } catch (error1) {
          err = error1;
          _data.callback(err, null);
        }
nanahira's avatar
nanahira committed
546 547
      }
    };
nanahira's avatar
nanahira committed
548 549 550 551 552 553 554 555 556
    challonge.matches._update = function(_data) {
      var err;
      try {
        challonge.matches.update(_data);
      } catch (error1) {
        err = error1;
        log.warn("Errored pushing scores to Challonge.", err);
      }
    };
nanahira's avatar
nanahira committed
557
    refresh_challonge_cache = global.refresh_challonge_cache = function() {
nanahira's avatar
nanahira committed
558 559 560 561
      if (settings.modules.challonge.cache_ttl) {
        challonge_cache[0] = null;
        challonge_cache[1] = null;
      }
nanahira's avatar
nanahira committed
562 563
    };
    refresh_challonge_cache();
nanahira's avatar
nanahira committed
564 565 566 567 568 569 570 571 572 573 574 575
    // challonge.participants._index({
    //   id: settings.modules.challonge.tournament_id,
    //   callback: (() ->
    //     challonge.matches._index({
    //       id: settings.modules.challonge.tournament_id,
    //       callback: (() ->
    //         return
    //       )
    //     })
    //     return
    //   )
    // })
nanahira's avatar
nanahira committed
576 577 578
    if (settings.modules.challonge.cache_ttl) {
      setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl);
    }
nanahira's avatar
nanahira committed
579 580
  }

nanahira's avatar
nanahira committed
581
  // 获取可用内存
nanahira's avatar
nanahira committed
582
  memory_usage = global.memory_usage = 0;
mercury233's avatar
mercury233 committed
583

nanahira's avatar
nanahira committed
584
  get_memory_usage = get_memory_usage = function() {
mercury233's avatar
mercury233 committed
585 586
    var prc_free;
    prc_free = exec("free");
mercury233's avatar
fix  
mercury233 committed
587
    prc_free.stdout.on('data', function(data) {
mercury233's avatar
mercury233 committed
588 589 590 591
      var actualFree, buffers, cached, free, line, lines, new_free, percentUsed, total;
      lines = data.toString().split(/\n/g);
      line = lines[0].split(/\s+/);
      new_free = line[6] === 'available' ? true : false;
mercury233's avatar
mercury233 committed
592 593 594 595
      line = lines[1].split(/\s+/);
      total = parseInt(line[1], 10);
      free = parseInt(line[3], 10);
      buffers = parseInt(line[5], 10);
mercury233's avatar
mercury233 committed
596 597 598 599 600 601
      if (new_free) {
        actualFree = parseInt(line[6], 10);
      } else {
        cached = parseInt(line[6], 10);
        actualFree = free + buffers + cached;
      }
mercury233's avatar
mercury233 committed
602
      percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2));
nanahira's avatar
nanahira committed
603
      memory_usage = global.memory_usage = percentUsed;
mercury233's avatar
mercury233 committed
604
    });
mercury233's avatar
mercury233 committed
605 606
  };

mercury233's avatar
mercury233 committed
607 608 609 610
  get_memory_usage();

  setInterval(get_memory_usage, 3000);

nanahira's avatar
nanahira committed
611
  Cloud_replay_ids = global.Cloud_replay_ids = [];
mercury233's avatar
mercury233 committed
612

nanahira's avatar
nanahira committed
613
  ROOM_all = global.ROOM_all = [];
mercury233's avatar
mercury233 committed
614

nanahira's avatar
nanahira committed
615
  ROOM_players_oppentlist = global.ROOM_players_oppentlist = {};
mercury233's avatar
mercury233 committed
616

nanahira's avatar
nanahira committed
617
  ROOM_players_banned = global.ROOM_players_banned = [];
mercury233's avatar
mercury233 committed
618

nanahira's avatar
nanahira committed
619
  ROOM_players_scores = global.ROOM_players_scores = {};
620

nanahira's avatar
nanahira committed
621
  ROOM_connected_ip = global.ROOM_connected_ip = {};
mercury233's avatar
mercury233 committed
622

nanahira's avatar
nanahira committed
623
  ROOM_bad_ip = global.ROOM_bad_ip = {};
mercury233's avatar
mercury233 committed
624

nanahira's avatar
nanahira committed
625
  // ban a user manually and permanently
nanahira's avatar
nanahira committed
626 627
  ban_user = global.ban_user = function(name, callback) {
    var bad_ip;
mercury233's avatar
mercury233 committed
628
    settings.ban.banned_user.push(name);
mercury233's avatar
mercury233 committed
629
    setting_save(settings);
nanahira's avatar
nanahira committed
630
    bad_ip = [];
nanahira's avatar
nanahira committed
631 632 633 634 635
    _async.each(ROOM_all, function(room, done) {
      if (!(room && room.established)) {
        done();
        return;
      }
nanahira's avatar
nanahira committed
636
      return _async.each(["players", "watchers"], function(player_type, _done) {
nanahira's avatar
nanahira committed
637 638 639 640 641
        return _async.each(room[player_type], function(player, __done) {
          if (player && (player.name === name || bad_ip.indexOf(player.ip) !== -1)) {
            bad_ip.push(player.ip);
            ROOM_bad_ip[bad_ip] = 99;
            settings.ban.banned_ip.push(player.ip);
nanahira's avatar
nanahira committed
642
            ygopro.stoc_send_chat_to_room(room, `${player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
643 644 645
            CLIENT_send_replays(player, room);
            CLIENT_kick(player);
          }
nanahira's avatar
nanahira committed
646 647 648 649
          return __done();
        }, _done);
      }, done);
    }, callback);
mercury233's avatar
mercury233 committed
650 651
  };

nanahira's avatar
nanahira committed
652 653
  // automatically ban user to use random duel
  ROOM_ban_player = global.ROOM_ban_player = function(name, ip, reason, countadd = 1) {
mercury233's avatar
mercury233 committed
654
    var bannedplayer, bantime;
655 656 657
    if (settings.modules.test_mode.no_ban_player) {
      return;
    }
mercury233's avatar
mercury233 committed
658 659 660 661
    bannedplayer = _.find(ROOM_players_banned, function(bannedplayer) {
      return ip === bannedplayer.ip;
    });
    if (bannedplayer) {
mercury233's avatar
mercury233 committed
662
      bannedplayer.count = bannedplayer.count + countadd;
mercury233's avatar
mercury233 committed
663 664 665 666 667 668 669 670 671 672 673 674
      bantime = bannedplayer.count > 3 ? Math.pow(2, bannedplayer.count - 3) * 2 : 0;
      bannedplayer.time = moment() < bannedplayer.time ? moment(bannedplayer.time).add(bantime, 'm') : moment().add(bantime, 'm');
      if (!_.find(bannedplayer.reasons, function(bannedreason) {
        return bannedreason === reason;
      })) {
        bannedplayer.reasons.push(reason);
      }
      bannedplayer.need_tip = true;
    } else {
      bannedplayer = {
        "ip": ip,
        "time": moment(),
mercury233's avatar
mercury233 committed
675
        "count": countadd,
mercury233's avatar
mercury233 committed
676 677 678 679 680 681 682
        "reasons": [reason],
        "need_tip": true
      };
      ROOM_players_banned.push(bannedplayer);
    }
  };

nanahira's avatar
nanahira committed
683
  //log.info("banned", name, ip, reason, bannedplayer.count)
nanahira's avatar
nanahira committed
684
  ROOM_kick = function(name, callback) {
nanahira's avatar
nanahira committed
685 686 687 688 689 690 691 692
    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
nanahira committed
693 694
      room.termiate();
      return done();
nanahira's avatar
nanahira committed
695
    }, function(err) {
nanahira's avatar
nanahira committed
696
      callback(null, found);
nanahira's avatar
nanahira committed
697 698 699
    });
  };

nanahira's avatar
nanahira committed
700
  ROOM_player_win = global.ROOM_player_win = function(name) {
701 702 703 704 705 706 707 708 709 710 711 712
    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
713
  ROOM_player_lose = global.ROOM_player_lose = function(name) {
714 715 716 717 718 719 720 721 722 723 724 725
    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
726
  ROOM_player_flee = global.ROOM_player_flee = function(name) {
727 728 729 730 731 732 733 734 735 736 737 738
    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
739
  ROOM_player_get_score = global.ROOM_player_get_score = function(player) {
740 741 742 743
    var name, score, total;
    name = player.name_vpass;
    score = ROOM_players_scores[name];
    if (!score) {
nanahira's avatar
nanahira committed
744
      return `${player.name} \${random_score_blank}`;
745
    }
mercury233's avatar
mercury233 committed
746
    total = score.win + score.lose;
747
    if (score.win < 2 && total < 3) {
nanahira's avatar
nanahira committed
748
      return `${player.name} \${random_score_not_enough}`;
749 750
    }
    if (score.combo >= 2) {
nanahira's avatar
nanahira committed
751
      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}`;
752
    } else {
nanahira's avatar
nanahira committed
753 754
      //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}`;
755 756 757
    }
  };

mercury233's avatar
mercury233 committed
758 759
  if (settings.modules.random_duel.post_match_scores) {
    setInterval(function() {
mercury233's avatar
mercury233 committed
760 761 762 763
      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;
nanahira's avatar
nanahira committed
764
      }).reverse(); // 败场由高到低
mercury233's avatar
mercury233 committed
765
      scores_by_win = _.sortBy(scores_by_lose, function(score) {
mercury233's avatar
mercury233 committed
766
        return score[1].win;
nanahira's avatar
nanahira committed
767
      }).reverse(); // 然后胜场由低到高,再逆转,就是先排胜场再排败场
mercury233's avatar
mercury233 committed
768
      scores = _.first(scores_by_win, 10);
nanahira's avatar
nanahira committed
769
      //log.info scores
mercury233's avatar
mercury233 committed
770 771 772 773 774 775
      request.post({
        url: settings.modules.random_duel.post_match_scores,
        form: {
          accesskey: settings.modules.random_duel.post_match_accesskey,
          rank: JSON.stringify(scores)
        }
nanahira's avatar
nanahira committed
776 777 778 779 780 781
      }, (error, response, body) => {
        if (error) {
          log.warn('RANDOM SCORE POST ERROR', error);
        } else {
          if (response.statusCode !== 204 && response.statusCode !== 200) {
            log.warn('RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body);
mercury233's avatar
mercury233 committed
782
          }
nanahira's avatar
nanahira committed
783 784 785 786
        }
      });
    //else
    //  log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
mercury233's avatar
mercury233 committed
787 788 789
    }, 60000);
  }

nanahira's avatar
nanahira committed
790
  ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = function(name, player_ip) {
mercury233's avatar
mercury233 committed
791 792
    var room, uname;
    uname = name.toUpperCase();
mercury233's avatar
mercury233 committed
793
    if (settings.modules.windbot.enabled && (uname.slice(0, 2) === 'AI' || (!settings.modules.random_duel.enabled && uname === ''))) {
mercury233's avatar
mercury233 committed
794 795
      return ROOM_find_or_create_ai(name);
    }
mercury233's avatar
mercury233 committed
796
    if (settings.modules.random_duel.enabled && (uname === '' || uname === 'S' || uname === 'M' || uname === 'T')) {
mercury233's avatar
mercury233 committed
797
      return ROOM_find_or_create_random(uname, player_ip);
mercury233's avatar
mercury233 committed
798 799 800
    }
    if (room = ROOM_find_by_name(name)) {
      return room;
mercury233's avatar
mercury233 committed
801
    } else if (memory_usage >= 90) {
mercury233's avatar
mercury233 committed
802 803 804 805 806 807
      return null;
    } else {
      return new Room(name);
    }
  };

nanahira's avatar
nanahira committed
808
  ROOM_find_or_create_random = global.ROOM_find_or_create_random = function(type, player_ip) {
mercury233's avatar
mercury233 committed
809 810 811 812 813 814 815
    var bannedplayer, max_player, name, playerbanned, result;
    bannedplayer = _.find(ROOM_players_banned, function(bannedplayer) {
      return player_ip === bannedplayer.ip;
    });
    if (bannedplayer) {
      if (bannedplayer.count > 6 && moment() < bannedplayer.time) {
        return {
nanahira's avatar
nanahira committed
816
          "error": `\${random_banned_part1}${bannedplayer.reasons.join('${random_ban_reason_separator}')}\${random_banned_part2}${moment(bannedplayer.time).fromNow(true)}\${random_banned_part3}`
mercury233's avatar
mercury233 committed
817 818
        };
      }
mercury233's avatar
mercury233 committed
819
      if (bannedplayer.count > 3 && moment() < bannedplayer.time && bannedplayer.need_tip && type !== 'T') {
mercury233's avatar
mercury233 committed
820 821
        bannedplayer.need_tip = false;
        return {
nanahira's avatar
nanahira committed
822
          "error": `\${random_deprecated_part1}${bannedplayer.reasons.join('${random_ban_reason_separator}')}\${random_deprecated_part2}${moment(bannedplayer.time).fromNow(true)}\${random_deprecated_part3}`
mercury233's avatar
mercury233 committed
823 824 825 826
        };
      } else if (bannedplayer.need_tip) {
        bannedplayer.need_tip = false;
        return {
nanahira's avatar
nanahira committed
827
          "error": `\${random_warn_part1}${bannedplayer.reasons.join('${random_ban_reason_separator}')}\${random_warn_part2}`
mercury233's avatar
mercury233 committed
828 829 830 831 832 833 834 835
        };
      } else if (bannedplayer.count > 2) {
        bannedplayer.need_tip = true;
      }
    }
    max_player = type === 'T' ? 4 : 2;
    playerbanned = bannedplayer && bannedplayer.count > 3 && moment() < bannedplayer.time;
    result = _.find(ROOM_all, function(room) {
836
      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
837 838
    });
    if (result) {
mercury233's avatar
mercury233 committed
839
      result.welcome = '${random_duel_enter_room_waiting}';
nanahira's avatar
nanahira committed
840
    //log.info 'found room', player_name
mercury233's avatar
mercury233 committed
841
    } else if (memory_usage < 90) {
842
      type = type ? type : settings.modules.random_duel.default_type;
mercury233's avatar
mercury233 committed
843 844 845 846
      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
847
      result.welcome = '${random_duel_enter_room_new}';
mercury233's avatar
mercury233 committed
848
      result.deprecated = playerbanned;
mercury233's avatar
mercury233 committed
849
    } else {
nanahira's avatar
nanahira committed
850
      //log.info 'create room', player_name, name
mercury233's avatar
mercury233 committed
851
      return null;
mercury233's avatar
mercury233 committed
852
    }
853 854 855
    if (result.random_type === 'S') {
      result.welcome2 = '${random_duel_enter_room_single}';
    }
mercury233's avatar
mercury233 committed
856
    if (result.random_type === 'M') {
857 858 859 860
      result.welcome2 = '${random_duel_enter_room_match}';
    }
    if (result.random_type === 'T') {
      result.welcome2 = '${random_duel_enter_room_tag}';
mercury233's avatar
mercury233 committed
861 862 863 864
    }
    return result;
  };

nanahira's avatar
nanahira committed
865
  ROOM_find_or_create_ai = global.ROOM_find_or_create_ai = function(name) {
866
    var ainame, namea, result, room, uname, windbot;
mercury233's avatar
mercury233 committed
867 868 869
    if (name === '') {
      name = 'AI';
    }
mercury233's avatar
mercury233 committed
870
    namea = name.split('#');
871
    uname = name.toUpperCase();
mercury233's avatar
mercury233 committed
872 873
    if (room = ROOM_find_by_name(name)) {
      return room;
874
    } else if (uname === 'AI') {
875 876 877
      windbot = _.sample(_.filter(windbots, function(w) {
        return !w.hidden;
      }));
mercury233's avatar
mercury233 committed
878
      name = 'AI#' + Math.floor(Math.random() * 100000);
mercury233's avatar
mercury233 committed
879 880
    } else if (namea.length > 1) {
      ainame = namea[namea.length - 1];
mercury233's avatar
mercury233 committed
881
      windbot = _.sample(_.filter(windbots, function(w) {
mercury233's avatar
mercury233 committed
882 883 884 885
        return w.name === ainame || w.deck === ainame;
      }));
      if (!windbot) {
        return {
mercury233's avatar
mercury233 committed
886
          "error": "${windbot_deck_not_found}"
mercury233's avatar
mercury233 committed
887 888
        };
      }
mercury233's avatar
mercury233 committed
889
      name = namea[0].toUpperCase() + '#N' + Math.floor(Math.random() * 100000);
mercury233's avatar
mercury233 committed
890
    } else {
891 892 893 894
      windbot = _.sample(_.filter(windbots, function(w) {
        return !w.hidden;
      }));
      name = name + '#' + Math.floor(Math.random() * 10000);
mercury233's avatar
mercury233 committed
895
    }
mercury233's avatar
mercury233 committed
896 897 898
    if (name.replace(/[^\x00-\xff]/g, "00").length > 20) {
      log.info("long ai name", name);
      return {
mercury233's avatar
mercury233 committed
899
        "error": "${windbot_name_too_long}"
mercury233's avatar
mercury233 committed
900 901
      };
    }
mercury233's avatar
mercury233 committed
902 903
    result = new Room(name);
    result.windbot = windbot;
nanahira's avatar
nanahira committed
904
    result.private = true;
mercury233's avatar
mercury233 committed
905 906 907
    return result;
  };

nanahira's avatar
nanahira committed
908
  ROOM_find_by_name = global.ROOM_find_by_name = function(name) {
mercury233's avatar
mercury233 committed
909 910
    var result;
    result = _.find(ROOM_all, function(room) {
911
      return room && room.name === name;
mercury233's avatar
mercury233 committed
912 913 914 915
    });
    return result;
  };

nanahira's avatar
nanahira committed
916
  ROOM_find_by_title = global.ROOM_find_by_title = function(title) {
mercury233's avatar
mercury233 committed
917 918 919 920 921 922 923
    var result;
    result = _.find(ROOM_all, function(room) {
      return room && room.title === title;
    });
    return result;
  };

nanahira's avatar
nanahira committed
924
  ROOM_find_by_port = global.ROOM_find_by_port = function(port) {
mercury233's avatar
mercury233 committed
925
    return _.find(ROOM_all, function(room) {
926
      return room && room.port === port;
mercury233's avatar
mercury233 committed
927 928 929
    });
  };

nanahira's avatar
nanahira committed
930 931 932 933 934 935
  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
936
  ROOM_validate = global.ROOM_validate = function(name) {
mercury233's avatar
mercury233 committed
937 938 939 940 941 942 943 944 945
    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;
946 947 948
      if (!room) {
        return false;
      }
mercury233's avatar
mercury233 committed
949 950 951 952 953 954 955
      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
956
  ROOM_unwelcome = global.ROOM_unwelcome = function(room, bad_player, reason) {
nanahira's avatar
nanahira committed
957
    var len2, m, player, ref2;
mercury233's avatar
mercury233 committed
958 959 960
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
961
    ref2 = room.players;
nanahira's avatar
nanahira committed
962 963
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      player = ref2[m];
mercury233's avatar
mercury233 committed
964
      if (player && player === bad_player) {
nanahira's avatar
nanahira committed
965
        ygopro.stoc_send_chat(player, `\${unwelcome_warn_part1}${reason}\${unwelcome_warn_part2}`, ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
966 967
      } else if (player && player.pos !== 7 && player !== bad_player) {
        player.flee_free = true;
nanahira's avatar
nanahira committed
968
        ygopro.stoc_send_chat(player, `\${unwelcome_tip_part1}${reason}\${unwelcome_tip_part2}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
969 970 971 972
      }
    }
  };

nanahira's avatar
nanahira committed
973
  CLIENT_kick = global.CLIENT_kick = function(client) {
974
    var room;
nanahira's avatar
nanahira committed
975 976 977
    if (!client) {
      return false;
    }
nanahira's avatar
nanahira committed
978
    client.system_kicked = true;
nanahira's avatar
typo  
nanahira committed
979
    if (settings.modules.reconnect.enabled && client.closed) {
nanahira's avatar
fix  
nanahira committed
980
      if (client.server && !client.had_new_reconnection) {
nanahira's avatar
fix  
nanahira committed
981
        room = ROOM_all[client.rid];
982 983 984
        if (room) {
          room.disconnect(client);
        } else {
nanahira's avatar
nanahira committed
985
          SERVER_kick(client.server);
986
        }
nanahira's avatar
fix  
nanahira committed
987
      }
nanahira's avatar
nanahira committed
988 989 990
    } else {
      client.destroy();
    }
nanahira's avatar
nanahira committed
991
    return true;
nanahira's avatar
nanahira committed
992 993
  };

nanahira's avatar
nanahira committed
994
  SERVER_kick = global.SERVER_kick = function(server) {
nanahira's avatar
typo  
nanahira committed
995
    if (!server) {
nanahira's avatar
nanahira committed
996 997 998 999 1000 1001 1002
      return false;
    }
    server.system_kicked = true;
    server.destroy();
    return true;
  };

nanahira's avatar
nanahira committed
1003
  release_disconnect = global.release_disconnect = function(dinfo, reconnected) {
nanahira's avatar
nanahira committed
1004 1005 1006 1007
    if (dinfo.old_client && !reconnected) {
      dinfo.old_client.destroy();
    }
    if (dinfo.old_server && !reconnected) {
nanahira's avatar
nanahira committed
1008
      SERVER_kick(dinfo.old_server);
nanahira's avatar
nanahira committed
1009 1010 1011 1012
    }
    clearTimeout(dinfo.timeout);
  };

nanahira's avatar
nanahira committed
1013
  CLIENT_get_authorize_key = global.CLIENT_get_authorize_key = function(client) {
nanahira's avatar
js  
nanahira committed
1014
    if (!settings.modules.mycard.enabled && client.vpass) {
1015
      return client.name_vpass;
nanahira's avatar
js  
nanahira committed
1016 1017
    } 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
1018
    } else {
1019
      return client.ip + ":" + client.name;
nanahira's avatar
nanahira committed
1020 1021 1022
    }
  };

nanahira's avatar
nanahira committed
1023
  CLIENT_reconnect_unregister = global.CLIENT_reconnect_unregister = function(client, reconnected, exact) {
nanahira's avatar
nanahira committed
1024 1025 1026 1027
    if (!settings.modules.reconnect.enabled) {
      return false;
    }
    if (disconnect_list[CLIENT_get_authorize_key(client)]) {
nanahira's avatar
nanahira committed
1028 1029 1030
      if (exact && disconnect_list[CLIENT_get_authorize_key(client)].old_client !== client) {
        return false;
      }
nanahira's avatar
nanahira committed
1031 1032 1033 1034 1035 1036 1037
      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
1038
  CLIENT_reconnect_register = global.CLIENT_reconnect_register = function(client, room_id, error) {
nanahira's avatar
nanahira committed
1039
    var dinfo, room, tmot;
nanahira's avatar
nanahira committed
1040
    room = ROOM_all[room_id];
nanahira's avatar
nanahira committed
1041 1042 1043
    if (client.had_new_reconnection) {
      return false;
    }
nanahira's avatar
nanahira committed
1044
    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
1045 1046
      return false;
    }
nanahira's avatar
nanahira committed
1047 1048 1049
    // 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
1050
    dinfo = {
nanahira's avatar
nanahira committed
1051
      room_id: room_id,
nanahira's avatar
nanahira committed
1052 1053 1054 1055 1056 1057
      old_client: client,
      old_server: client.server,
      deckbuf: client.start_deckbuf
    };
    tmot = setTimeout(function() {
      room.disconnect(client, error);
nanahira's avatar
nanahira committed
1058
    //SERVER_kick(dinfo.old_server)
nanahira's avatar
nanahira committed
1059 1060 1061
    }, settings.modules.reconnect.wait_time);
    dinfo.timeout = tmot;
    disconnect_list[CLIENT_get_authorize_key(client)] = dinfo;
nanahira's avatar
nanahira committed
1062 1063
    //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
1064 1065 1066 1067
    if (client.time_confirm_required) {
      client.time_confirm_required = false;
      ygopro.ctos_send(client.server, 'TIME_CONFIRM');
    }
nanahira's avatar
nanahira committed
1068
    if (settings.modules.reconnect.auto_surrender_after_disconnect && room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
1069 1070
      ygopro.ctos_send(client.server, 'SURRENDER');
    }
nanahira's avatar
nanahira committed
1071 1072 1073
    return true;
  };

nanahira's avatar
nanahira committed
1074
  CLIENT_import_data = global.CLIENT_import_data = function(client, old_client, room) {
nanahira's avatar
nanahira committed
1075
    var index, key, len2, len3, m, n, player, ref2;
nanahira's avatar
nanahira committed
1076 1077 1078 1079 1080 1081 1082 1083 1084
    ref2 = room.players;
    for (index = m = 0, len2 = ref2.length; m < len2; index = ++m) {
      player = ref2[index];
      if (player === old_client) {
        room.players[index] = client;
        break;
      }
    }
    room.dueling_players[old_client.pos] = client;
nanahira's avatar
nanahira committed
1085
    if (room.waiting_for_player === old_client) {
nanahira's avatar
nanahira committed
1086 1087
      room.waiting_for_player = client;
    }
nanahira's avatar
nanahira committed
1088
    if (room.waiting_for_player2 === old_client) {
nanahira's avatar
nanahira committed
1089 1090
      room.waiting_for_player2 = client;
    }
nanahira's avatar
nanahira committed
1091
    if (room.selecting_tp === old_client) {
nanahira's avatar
nanahira committed
1092 1093
      room.selecting_tp = client;
    }
nanahira's avatar
nanahira committed
1094 1095 1096
    if (room.determine_firstgo === old_client) {
      room.determine_firstgo = client;
    }
nanahira's avatar
nanahira committed
1097 1098 1099 1100
    for (n = 0, len3 = import_datas.length; n < len3; n++) {
      key = import_datas[n];
      client[key] = old_client[key];
    }
nanahira's avatar
nanahira committed
1101 1102 1103
    old_client.had_new_reconnection = true;
  };

nanahira's avatar
nanahira committed
1104
  SERVER_clear_disconnect = global.SERVER_clear_disconnect = function(server) {
nanahira's avatar
nanahira committed
1105 1106
    var k, v;
    if (!settings.modules.reconnect.enabled) {
nanahira's avatar
nanahira committed
1107
      return false;
nanahira's avatar
nanahira committed
1108 1109 1110 1111 1112 1113
    }
    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
1114
        return true;
nanahira's avatar
nanahira committed
1115 1116
      }
    }
nanahira's avatar
nanahira committed
1117 1118 1119
    return false;
  };

nanahira's avatar
nanahira committed
1120
  ROOM_clear_disconnect = global.ROOM_clear_disconnect = function(room_id) {
nanahira's avatar
nanahira committed
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
    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
1134 1135
  };

nanahira's avatar
nanahira committed
1136
  CLIENT_is_player = global.CLIENT_is_player = function(client, room) {
nanahira's avatar
nanahira committed
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
    var is_player, len2, m, player, ref2;
    is_player = false;
    ref2 = room.players;
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      player = ref2[m];
      if (client === player) {
        is_player = true;
        break;
      }
    }
nanahira's avatar
fix  
nanahira committed
1147
    return is_player && client.pos <= 3;
nanahira's avatar
nanahira committed
1148 1149
  };

nanahira's avatar
nanahira committed
1150
  CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1151
    var disconnect_info, room;
nanahira's avatar
nanahira committed
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
    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
1162 1163 1164 1165 1166
    room = ROOM_all[disconnect_info.room_id];
    if (!room) {
      CLIENT_reconnect_unregister(client);
      return false;
    }
nanahira's avatar
nanahira committed
1167 1168 1169 1170 1171 1172
    if (deckbuf && !_.isEqual(deckbuf, disconnect_info.deckbuf)) {
      return false;
    }
    return true;
  };

nanahira's avatar
nanahira committed
1173
  CLIENT_get_kick_reconnect_target = global.CLIENT_get_kick_reconnect_target = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1174 1175 1176
    var len2, len3, m, n, player, ref2, room;
    for (m = 0, len2 = ROOM_all.length; m < len2; m++) {
      room = ROOM_all[m];
nanahira's avatar
nanahira committed
1177
      if (room && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && !room.windbot) {
nanahira's avatar
nanahira committed
1178 1179 1180
        ref2 = room.get_playing_player();
        for (n = 0, len3 = ref2.length; n < len3; n++) {
          player = ref2[n];
1181
          if (!player.closed && player.name === client.name && (settings.modules.challonge.enabled || player.pass === client.pass) && (settings.modules.mycard.enabled || settings.modules.tournament_mode.enabled || player.ip === client.ip || (client.vpass && client.vpass === player.vpass)) && (!deckbuf || _.isEqual(player.start_deckbuf, deckbuf))) {
nanahira's avatar
nanahira committed
1182 1183 1184 1185 1186 1187 1188 1189
            return player;
          }
        }
      }
    }
    return null;
  };

nanahira's avatar
nanahira committed
1190
  CLIENT_is_able_to_kick_reconnect = global.CLIENT_is_able_to_kick_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1191 1192 1193 1194 1195 1196
    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
1197 1198 1199
    return true;
  };

nanahira's avatar
nanahira committed
1200
  CLIENT_send_pre_reconnect_info = global.CLIENT_send_pre_reconnect_info = function(client, room, old_client) {
nanahira's avatar
nanahira committed
1201
    var len2, m, player, ref2, req_pos;
nanahira's avatar
nanahira committed
1202
    ygopro.stoc_send_chat(client, "${pre_reconnecting_to_room}", ygopro.constants.COLORS.BABYBLUE);
1203
    ygopro.stoc_send(client, 'JOIN_GAME', room.join_game_buffer);
nanahira's avatar
nanahira committed
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
    req_pos = old_client.pos;
    if (old_client.is_host) {
      req_pos += 0x10;
    }
    ygopro.stoc_send(client, 'TYPE_CHANGE', {
      type: req_pos
    });
    ref2 = room.players;
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      player = ref2[m];
nanahira's avatar
nanahira committed
1214
      ygopro.stoc_send(client, 'HS_PLAYER_ENTER', {
nanahira's avatar
nanahira committed
1215 1216
        name: player.name,
        pos: player.pos
nanahira's avatar
nanahira committed
1217
      });
nanahira's avatar
nanahira committed
1218 1219 1220
    }
  };

nanahira's avatar
nanahira committed
1221
  CLIENT_send_reconnect_info = global.CLIENT_send_reconnect_info = function(client, server, room) {
nanahira's avatar
nanahira committed
1222 1223
    client.reconnecting = true;
    ygopro.stoc_send_chat(client, "${reconnecting_to_room}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
    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
1234
        if (client === room.selecting_tp) { // and !client.selected_preduel
nanahira's avatar
nanahira committed
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
          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
1249 1250 1251
    }
  };

nanahira's avatar
nanahira committed
1252
  CLIENT_pre_reconnect = global.CLIENT_pre_reconnect = function(client) {
nanahira's avatar
nanahira committed
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
    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
1266 1267 1268
    }
  };

nanahira's avatar
nanahira committed
1269
  CLIENT_reconnect = global.CLIENT_reconnect = function(client) {
nanahira's avatar
nanahira committed
1270
    var current_old_server, dinfo, room;
nanahira's avatar
nanahira committed
1271 1272 1273 1274 1275 1276 1277
    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
1278
    room = ROOM_all[dinfo.room_id];
nanahira's avatar
nanahira committed
1279 1280 1281 1282 1283 1284
    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
1285
    SERVER_kick(current_old_server);
nanahira's avatar
nanahira committed
1286 1287
    client.established = true;
    client.pre_establish_buffers = [];
nanahira's avatar
nanahira committed
1288 1289 1290
    if (room.random_type || room.arena) {
      room.last_active_time = moment();
    }
nanahira's avatar
nanahira committed
1291 1292
    CLIENT_import_data(client, dinfo.old_client, room);
    CLIENT_send_reconnect_info(client, client.server, room);
nanahira's avatar
nanahira committed
1293 1294
    //console.log("#{client.name} ${reconnect_to_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${reconnect_to_game}`);
nanahira's avatar
nanahira committed
1295 1296 1297
    CLIENT_reconnect_unregister(client, true);
  };

nanahira's avatar
nanahira committed
1298
  CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = function(client, deckbuf) {
nanahira's avatar
nanahira committed
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316
    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
1317
    SERVER_kick(current_old_server);
nanahira's avatar
nanahira committed
1318 1319 1320 1321 1322 1323 1324
    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
1325 1326
    //console.log("#{client.name} ${reconnect_to_game}")
    ygopro.stoc_send_chat_to_room(room, `${client.name} \${reconnect_to_game}`);
nanahira's avatar
nanahira committed
1327 1328 1329
    CLIENT_reconnect_unregister(client, true);
  };

nanahira's avatar
nanahira committed
1330
  if (settings.modules.reconnect.enabled) {
nanahira's avatar
nanahira committed
1331
    disconnect_list = {}; // {old_client, old_server, room_id, timeout, deckbuf}
nanahira's avatar
nanahira committed
1332 1333
  }

nanahira's avatar
nanahira committed
1334
  CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = function(client) {
nanahira's avatar
nanahira committed
1335 1336 1337 1338 1339
    if (!settings.modules.heartbeat_detection.enabled || !client.heartbeat_timeout) {
      return false;
    }
    clearTimeout(client.heartbeat_timeout);
    delete client.heartbeat_timeout;
nanahira's avatar
nanahira committed
1340
    //log.info(2, client.name)
nanahira's avatar
nanahira committed
1341 1342 1343
    return true;
  };

nanahira's avatar
nanahira committed
1344
  CLIENT_heartbeat_register = global.CLIENT_heartbeat_register = function(client, send) {
1345
    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
1346 1347 1348 1349 1350 1351 1352 1353
      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
1354 1355 1356 1357 1358
        player: 0,
        left_time: 0
      });
      ygopro.stoc_send(client, "TIME_LIMIT", {
        player: 1,
nanahira's avatar
nanahira committed
1359 1360 1361 1362 1363 1364 1365 1366
        left_time: 0
      });
    }
    client.heartbeat_timeout = setTimeout(function() {
      CLIENT_heartbeat_unregister(client);
      if (!(client.closed || client.heartbeat_responsed)) {
        client.destroy();
      }
1367
    }, settings.modules.heartbeat_detection.wait_time);
nanahira's avatar
nanahira committed
1368
    //log.info(1, client.name)
nanahira's avatar
nanahira committed
1369 1370 1371
    return true;
  };

nanahira's avatar
nanahira committed
1372
  CLIENT_is_banned_by_mc = global.CLIENT_is_banned_by_mc = function(client) {
nanahira's avatar
nanahira committed
1373 1374 1375
    return client.ban_mc && client.ban_mc.banned && moment().isBefore(client.ban_mc.until);
  };

nanahira's avatar
nanahira committed
1376
  CLIENT_send_replays = global.CLIENT_send_replays = function(client, room) {
nanahira's avatar
nanahira committed
1377
    var buffer, i, len2, m, ref2;
1378
    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
1379 1380
      return false;
    }
nanahira's avatar
nanahira committed
1381
    client.replays_sent = true;
nanahira's avatar
nanahira committed
1382 1383 1384 1385 1386
    i = 0;
    ref2 = room.replays;
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      buffer = ref2[m];
      ++i;
nanahira's avatar
fix  
nanahira committed
1387 1388 1389 1390
      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
1391 1392 1393 1394
    }
    return true;
  };

nanahira's avatar
nanahira committed
1395
  SOCKET_flush_data = global.SOCKET_flush_data = function(sk, datas) {
1396
    var buffer, len2, m;
nanahira's avatar
js  
nanahira committed
1397 1398 1399
    if (!sk || sk.closed) {
      return false;
    }
1400 1401 1402 1403 1404
    for (m = 0, len2 = datas.length; m < len2; m++) {
      buffer = datas[m];
      sk.write(buffer);
    }
    datas.splice(0, datas.length);
nanahira's avatar
js  
nanahira committed
1405
    return true;
1406 1407
  };

nanahira's avatar
nanahira committed
1408 1409
  Room = class Room {
    constructor(name, hostinfo) {
nanahira's avatar
nanahira committed
1410
      var death_time, draw_count, duel_log_id, duel_rule, lflist, param, rule, start_hand, start_lp, time_limit;
mercury233's avatar
mercury233 committed
1411 1412
      this.hostinfo = hostinfo;
      this.name = name;
nanahira's avatar
nanahira committed
1413
      //@alive = true
mercury233's avatar
mercury233 committed
1414 1415 1416
      this.players = [];
      this.player_datas = [];
      this.status = 'starting';
nanahira's avatar
nanahira committed
1417
      //@started = false
mercury233's avatar
mercury233 committed
1418 1419 1420
      this.established = false;
      this.watcher_buffers = [];
      this.recorder_buffers = [];
1421
      this.cloud_replay_id = Math.floor(Math.random() * 100000000);
mercury233's avatar
mercury233 committed
1422 1423 1424
      this.watchers = [];
      this.random_type = '';
      this.welcome = '';
mercury233's avatar
mercury233 committed
1425
      this.scores = {};
nanahira's avatar
nanahira committed
1426
      this.decks = {};
1427
      this.duel_count = 0;
nanahira's avatar
nanahira committed
1428
      this.death = 0;
nanahira's avatar
nanahira committed
1429
      this.turn = 0;
nanahira's avatar
nanahira committed
1430
      this.duel_stage = ygopro.constants.DUEL_STAGE.BEGIN;
nanahira's avatar
nanahira committed
1431
      this.replays = [];
nanahira's avatar
nanahira committed
1432
      this.first_list = [];
mercury233's avatar
mercury233 committed
1433
      ROOM_all.push(this);
mercury233's avatar
fix  
mercury233 committed
1434
      this.hostinfo || (this.hostinfo = JSON.parse(JSON.stringify(settings.hostinfo)));
nanahira's avatar
nanahira committed
1435
      delete this.hostinfo.comment;
mercury233's avatar
mercury233 committed
1436
      if (lflists.length) {
1437
        if (this.hostinfo.rule === 1 && this.hostinfo.lflist === 0) {
mercury233's avatar
mercury233 committed
1438
          this.hostinfo.lflist = _.findIndex(lflists, function(list) {
1439 1440 1441 1442 1443 1444
            return list.tcg;
          });
        }
      } else {
        this.hostinfo.lflist = -1;
      }
mercury233's avatar
mercury233 committed
1445 1446 1447 1448 1449
      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
1450 1451 1452
      } else if (name.slice(0, 3) === 'AI#') {
        this.hostinfo.rule = 2;
        this.hostinfo.lflist = -1;
mercury233's avatar
mercury233 committed
1453
        this.hostinfo.time_limit = 999;
mercury233's avatar
mercury233 committed
1454 1455 1456
      } 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]);
1457
        this.hostinfo.duel_rule = (param[3] === 'T' ? 3 : 4);
mercury233's avatar
mercury233 committed
1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
        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
1474
          this.hostinfo.lflist = _.findIndex(lflists, function(list) {
mercury233's avatar
mercury233 committed
1475
            return list.tcg;
mercury233's avatar
mercury233 committed
1476 1477 1478 1479
          });
        }
        if (rule.match(/(^|,|,)(OCGONLY|OO)(,|,|$)/)) {
          this.hostinfo.rule = 0;
1480
          this.hostinfo.lflist = 0;
mercury233's avatar
mercury233 committed
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
        }
        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
1541
        if (rule.match(/(^|,|,)(IGPRIORITY|PR)(,|,|$)/)) { // deprecated
nanahira's avatar
nanahira committed
1542
          this.hostinfo.duel_rule = 4;
1543 1544 1545
        }
        if ((param = rule.match(/(^|,|,)(DUELRULE|MR)(\d+)(,|,|$)/))) {
          duel_rule = parseInt(param[3]);
mercury233's avatar
mercury233 committed
1546
          if (duel_rule && duel_rule > 0 && duel_rule <= 5) {
1547 1548
            this.hostinfo.duel_rule = duel_rule;
          }
mercury233's avatar
mercury233 committed
1549
        }
nanahira's avatar
nanahira committed
1550
        if (rule.match(/(^|,|,)(NOWATCH|NW)(,|,|$)/)) {
nanahira's avatar
nanahira committed
1551
          this.hostinfo.no_watch = true;
nanahira's avatar
nanahira committed
1552
        }
nanahira's avatar
nanahira committed
1553 1554 1555 1556 1557 1558 1559 1560
        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
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
        if (settings.modules.tournament_mode.enable_recover && (param = rule.match(/(^|,|,)(RC|RECOVER)(\d*)%(\d*)(,|,|$)/))) {
          this.recovered = true;
          this.recovering = true;
          this.recover_from_turn = parseInt(param[4]);
          duel_log_id = parseInt(param[3]);
          this.recover_duel_log = _.find(duel_log.duel_log, function(duel) {
            return duel.id === duel_log_id && duel.roommode !== 2;
          });
          if (!this.recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replay_filename)) {
            this.error = "${cloud_replay_no}";
            return;
          }
          this.recover_replay = ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replay_filename);
          this.recover_buffers = [[], [], [], []];
          this.welcome = "${recover_hint}";
        }
mercury233's avatar
mercury233 committed
1577
      }
nanahira's avatar
nanahira committed
1578
      this.hostinfo.replay_mode = 0; // 0x1: Save the replays in file. 0x2: Block the replays to observers.
1579 1580 1581
      if (settings.modules.tournament_mode.enabled) {
        this.hostinfo.replay_mode |= 0x1;
      }
1582
      if ((settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player) || (this.hostinfo.mode === 1 && settings.modules.replay_delay)) {
1583 1584
        this.hostinfo.replay_mode |= 0x2;
      }
1585
      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
nanahira committed
1586 1587 1588
      if (this.recovered) {
        param.push(this.recover_replay.header.seed);
      }
mercury233's avatar
mercury233 committed
1589 1590
      try {
        this.process = spawn('./ygopro', param, {
mercury233's avatar
mercury233 committed
1591
          cwd: 'ygopro'
mercury233's avatar
mercury233 committed
1592
        });
nanahira's avatar
nanahira committed
1593
        this.process_pid = this.process.pid;
nanahira's avatar
nanahira committed
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
        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
1606
        this.process.stdout.setEncoding('utf8');
nanahira's avatar
nanahira committed
1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622
        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() {
              var buffer, len2, m, ref2;
              ref2 = player.pre_establish_buffers;
              for (m = 0, len2 = ref2.length; m < len2; m++) {
                buffer = ref2[m];
                player.server.write(buffer);
              }
              player.established = true;
              player.pre_establish_buffers = [];
mercury233's avatar
mercury233 committed
1623
            });
nanahira's avatar
nanahira committed
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642
          });
          if (this.windbot) {
            setTimeout(() => {
              return this.add_windbot(this.windbot);
            }, 200);
          }
        });
        this.process.stderr.on('data', (data) => {
          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
1643
      } catch (error1) {
mercury233's avatar
mercury233 committed
1644
        this.error = "${create_room_failed}";
mercury233's avatar
mercury233 committed
1645 1646 1647
      }
    }

nanahira's avatar
nanahira committed
1648
    delete() {
nanahira's avatar
fix  
nanahira committed
1649
      var end_time, formatted_replays, index, len2, log_rep_id, m, name, player_ips, player_names, recorder_buffer, ref2, ref3, repbuf, replay_id, room_name, score, score_array, score_form;
mercury233's avatar
mercury233 committed
1650 1651 1652
      if (this.deleted) {
        return;
      }
nanahira's avatar
nanahira committed
1653
      //log.info 'room-delete', this.name, ROOM_all.length
1654
      score_array = [];
nanahira's avatar
nanahira committed
1655 1656 1657
      ref2 = this.scores;
      for (name in ref2) {
        score = ref2[name];
nanahira's avatar
nanahira committed
1658
        score_form = {
1659
          name: name.split('$')[0],
nanahira's avatar
nanahira committed
1660
          score: score,
1661 1662
          deck: null,
          name_vpass: name
nanahira's avatar
nanahira committed
1663 1664 1665 1666 1667
        };
        if (this.decks[name]) {
          score_form.deck = this.decks[name];
        }
        score_array.push(score_form);
1668
      }
1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680
      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
1681 1682
        if (score_array.length === 1) { // same name
          //log.info score_array[0].name
1683 1684 1685 1686
          ROOM_player_win(score_array[0].name_vpass);
          ROOM_player_lose(score_array[0].name_vpass);
        }
      }
nanahira's avatar
-5  
nanahira committed
1687
      if (settings.modules.arena_mode.enabled && this.arena) {
nanahira's avatar
nanahira committed
1688
        //log.info 'SCORE', score_array, @start_time
nanahira's avatar
-5  
nanahira committed
1689 1690 1691 1692 1693 1694 1695
        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
1696
              name: null,
nanahira's avatar
nanahira committed
1697 1698
              score: -5,
              deck: null
nanahira's avatar
-5  
nanahira committed
1699
            };
1700
          }
nanahira's avatar
-5  
nanahira committed
1701 1702
          if (!score_array[1]) {
            score_array[1] = {
nanahira's avatar
nanahira committed
1703
              name: null,
nanahira's avatar
nanahira committed
1704 1705
              score: -5,
              deck: null
mercury233's avatar
merge  
mercury233 committed
1706
            };
nanahira's avatar
-5  
nanahira committed
1707 1708 1709
          }
          score_array[0].score = -5;
          score_array[1].score = -5;
mercury233's avatar
merge  
mercury233 committed
1710
        }
nanahira's avatar
fix  
nanahira committed
1711 1712 1713 1714 1715 1716 1717 1718
        formatted_replays = [];
        ref3 = this.replays;
        for (m = 0, len2 = ref3.length; m < len2; m++) {
          repbuf = ref3[m];
          if (repbuf) {
            formatted_replays.push(repbuf.toString("base64"));
          }
        }
nanahira's avatar
-5  
nanahira committed
1719 1720 1721 1722 1723 1724 1725 1726
        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
1727 1728
            userdeckA: score_array[0].deck,
            userdeckB: score_array[1].deck,
nanahira's avatar
nanahira committed
1729 1730
            first: JSON.stringify(this.first_list),
            replays: JSON.stringify(formatted_replays),
nanahira's avatar
-5  
nanahira committed
1731 1732 1733 1734
            start: this.start_time,
            end: end_time,
            arena: this.arena
          }
nanahira's avatar
nanahira committed
1735 1736 1737 1738 1739 1740
        }, (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
1741
            }
nanahira's avatar
nanahira committed
1742 1743
          }
        });
mercury233's avatar
mercury233 committed
1744
      }
nanahira's avatar
nanahira committed
1745 1746
      //else
      //  log.info 'SCORE POST OK', response.statusCode, response.statusMessage, @name, body
nanahira's avatar
nanahira committed
1747
      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
1748
        room_name = this.name;
nanahira's avatar
nanahira committed
1749
        challonge.matches._update({
1750
          id: settings.modules.challonge.tournament_id,
nanahira's avatar
nanahira committed
1751
          matchId: this.challonge_info.id,
nanahira's avatar
nanahira committed
1752
          match: this.get_challonge_score(),
nanahira's avatar
nanahira committed
1753 1754
          callback: function(err, data) {
            if (err) {
nanahira's avatar
js  
nanahira committed
1755
              log.warn("Errored pushing scores to Challonge.", room_name, err);
nanahira's avatar
nanahira committed
1756 1757
            } else {
              refresh_challonge_cache();
nanahira's avatar
nanahira committed
1758 1759 1760 1761
            }
          }
        });
      }
mercury233's avatar
mercury233 committed
1762
      if (this.player_datas.length && settings.modules.cloud_replay.enabled) {
1763
        replay_id = this.cloud_replay_id;
mercury233's avatar
mercury233 committed
1764 1765 1766
        if (this.has_ygopro_error) {
          log_rep_id = true;
        }
mercury233's avatar
mercury233 committed
1767 1768 1769
        player_names = this.player_datas[0].name + (this.player_datas[2] ? "+" + this.player_datas[2].name : "") + " VS " + (this.player_datas[1] ? this.player_datas[1].name : "AI") + (this.player_datas[3] ? "+" + this.player_datas[3].name : "");
        player_ips = [];
        _.each(this.player_datas, function(player) {
1770
          player_ips.push(player.key);
mercury233's avatar
mercury233 committed
1771 1772 1773
        });
        recorder_buffer = Buffer.concat(this.recorder_buffers);
        zlib.deflate(recorder_buffer, function(err, replay_buffer) {
1774
          var date_time, recorded_ip;
mercury233's avatar
mercury233 committed
1775
          replay_buffer = replay_buffer.toString('binary');
nanahira's avatar
nanahira committed
1776
          //log.info err, replay_buffer
mercury233's avatar
mercury233 committed
1777
          date_time = moment().format('YYYY-MM-DD HH:mm:ss');
nanahira's avatar
nanahira committed
1778
          //replay_id=Math.floor(Math.random()*100000000)
mercury233's avatar
mercury233 committed
1779
          redisdb.hmset("replay:" + replay_id, "replay_id", replay_id, "replay_buffer", replay_buffer, "player_names", player_names, "date_time", date_time);
nanahira's avatar
nanahira committed
1780
          if (!log_rep_id && !settings.modules.cloud_replay.never_expire) {
mercury233's avatar
mercury233 committed
1781 1782
            redisdb.expire("replay:" + replay_id, 60 * 60 * 24);
          }
mercury233's avatar
mercury233 committed
1783 1784 1785 1786 1787 1788 1789 1790
          recorded_ip = [];
          _.each(player_ips, function(player_ip) {
            if (_.contains(recorded_ip, player_ip)) {
              return;
            }
            recorded_ip.push(player_ip);
            redisdb.lpush(player_ip + ":replays", replay_id);
          });
mercury233's avatar
mercury233 committed
1791
          if (log_rep_id) {
mercury233's avatar
mercury233 committed
1792 1793
            log.info("error replay: R#" + replay_id);
          }
mercury233's avatar
mercury233 committed
1794 1795 1796 1797 1798 1799
        });
      }
      this.watcher_buffers = [];
      this.recorder_buffers = [];
      this.players = [];
      if (this.watcher) {
mercury233's avatar
mercury233 committed
1800
        this.watcher.destroy();
mercury233's avatar
mercury233 committed
1801
      }
mercury233's avatar
fix  
mercury233 committed
1802 1803 1804
      if (this.recorder) {
        this.recorder.destroy();
      }
mercury233's avatar
mercury233 committed
1805 1806
      this.deleted = true;
      index = _.indexOf(ROOM_all, this);
nanahira's avatar
nanahira committed
1807
      if (settings.modules.reconnect.enabled) {
nanahira's avatar
nanahira committed
1808
        ROOM_clear_disconnect(index);
nanahira's avatar
nanahira committed
1809
      }
mercury233's avatar
mercury233 committed
1810
      if (index !== -1) {
1811
        ROOM_all[index] = null;
mercury233's avatar
mercury233 committed
1812
      }
mercury233's avatar
mercury233 committed
1813
      if (!this.windbot && this.established && settings.modules.http.websocket_roomlist) {
nanahira's avatar
nanahira committed
1814 1815
        //ROOM_all.splice(index, 1) unless index == -1
        roomlist.delete(this);
mercury233's avatar
mercury233 committed
1816
      }
nanahira's avatar
nanahira committed
1817
    }
mercury233's avatar
mercury233 committed
1818

nanahira's avatar
nanahira committed
1819
    get_playing_player() {
mercury233's avatar
mercury233 committed
1820 1821 1822 1823 1824 1825 1826 1827
      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
1828
    }
mercury233's avatar
mercury233 committed
1829

nanahira's avatar
nanahira committed
1830
    get_host() {
mercury233's avatar
mercury233 committed
1831 1832 1833 1834 1835 1836 1837 1838
      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
1839
    }
mercury233's avatar
mercury233 committed
1840

nanahira's avatar
nanahira committed
1841
    get_disconnected_count() {
1842 1843
      var found, len2, m, player, ref2;
      if (!settings.modules.reconnect.enabled) {
1844
        return 0;
1845
      }
1846
      found = 0;
1847 1848 1849
      ref2 = this.get_playing_player();
      for (m = 0, len2 = ref2.length; m < len2; m++) {
        player = ref2[m];
1850 1851
        if (player.closed) {
          found++;
1852 1853 1854
        }
      }
      return found;
nanahira's avatar
nanahira committed
1855
    }
1856

nanahira's avatar
nanahira committed
1857
    get_challonge_score() {
nanahira's avatar
nanahira committed
1858
      var challonge_duel_log;
nanahira's avatar
nanahira committed
1859
      if (!settings.modules.challonge.enabled || this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || this.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
1860 1861 1862
        return null;
      }
      challonge_duel_log = {};
1863
      if (this.scores[this.dueling_players[0].name_vpass] > this.scores[this.dueling_players[1].name_vpass]) {
nanahira's avatar
nanahira committed
1864
        challonge_duel_log.winnerId = this.dueling_players[0].challonge_info.id;
1865
      } else if (this.scores[this.dueling_players[0].name_vpass] < this.scores[this.dueling_players[1].name_vpass]) {
nanahira's avatar
nanahira committed
1866 1867 1868 1869 1870 1871
        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) {
1872
          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
1873
        } else if (this.dueling_players[1].challonge_info.id === this.challonge_info.player1Id && this.dueling_players[0].challonge_info.id === this.challonge_info.player2Id) {
1874
          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
1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888
        } 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
1889
    }
nanahira's avatar
nanahira committed
1890

nanahira's avatar
nanahira committed
1891
    get_old_hostinfo() { // Just for supporting websocket roomlist in old MyCard client....
nanahira's avatar
nanahira committed
1892 1893
      var ret;
      ret = _.clone(this.hostinfo);
nanahira's avatar
nanahira committed
1894
      ret.enable_priority = this.hostinfo.duel_rule !== 5;
nanahira's avatar
nanahira committed
1895
      return ret;
nanahira's avatar
nanahira committed
1896
    }
nanahira's avatar
nanahira committed
1897

nanahira's avatar
nanahira committed
1898
    send_replays() {
nanahira's avatar
nanahira committed
1899 1900 1901 1902 1903 1904 1905
      var len2, len3, m, n, player, ref2, ref3;
      if (!(settings.modules.replay_delay && this.replays.length && this.hostinfo.mode === 1)) {
        return false;
      }
      ref2 = this.players;
      for (m = 0, len2 = ref2.length; m < len2; m++) {
        player = ref2[m];
nanahira's avatar
nanahira committed
1906 1907 1908
        if (player) {
          CLIENT_send_replays(player, this);
        }
nanahira's avatar
nanahira committed
1909 1910 1911 1912
      }
      ref3 = this.watchers;
      for (n = 0, len3 = ref3.length; n < len3; n++) {
        player = ref3[n];
nanahira's avatar
nanahira committed
1913 1914 1915
        if (player) {
          CLIENT_send_replays(player, this);
        }
nanahira's avatar
nanahira committed
1916 1917
      }
      return true;
nanahira's avatar
nanahira committed
1918
    }
nanahira's avatar
nanahira committed
1919

nanahira's avatar
nanahira committed
1920
    add_windbot(botdata) {
mercury233's avatar
mercury233 committed
1921 1922
      this.windbot = botdata;
      request({
nanahira's avatar
nanahira committed
1923 1924 1925 1926 1927 1928 1929 1930
        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
1931

nanahira's avatar
nanahira committed
1932 1933 1934
    //else
    //log.info "windbot added"
    connect(client) {
mercury233's avatar
mercury233 committed
1935
      var host_player;
mercury233's avatar
mercury233 committed
1936
      this.players.push(client);
nanahira's avatar
nanahira committed
1937
      client.join_time = moment();
mercury233's avatar
mercury233 committed
1938
      if (this.random_type) {
mercury233's avatar
mercury233 committed
1939
        client.abuse_count = 0;
mercury233's avatar
mercury233 committed
1940 1941
        host_player = this.get_host();
        if (host_player && (host_player !== client)) {
nanahira's avatar
nanahira committed
1942
          // 进来时已经有人在等待了,互相记录为匹配过
mercury233's avatar
mercury233 committed
1943 1944
          ROOM_players_oppentlist[host_player.ip] = client.ip;
          ROOM_players_oppentlist[client.ip] = host_player.ip;
mercury233's avatar
mercury233 committed
1945
        } else {
nanahira's avatar
nanahira committed
1946
          // 第一个玩家刚进来,还没就位
mercury233's avatar
mercury233 committed
1947
          ROOM_players_oppentlist[client.ip] = null;
mercury233's avatar
mercury233 committed
1948 1949 1950
        }
      }
      if (this.established) {
nanahira's avatar
nanahira committed
1951
        if (!this.windbot && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.http.websocket_roomlist) {
mercury233's avatar
mercury233 committed
1952 1953 1954
          roomlist.update(this);
        }
        client.server.connect(this.port, '127.0.0.1', function() {
nanahira's avatar
nanahira committed
1955
          var buffer, len2, m, ref2;
nanahira's avatar
nanahira committed
1956
          ref2 = client.pre_establish_buffers;
nanahira's avatar
nanahira committed
1957 1958
          for (m = 0, len2 = ref2.length; m < len2; m++) {
            buffer = ref2[m];
mercury233's avatar
mercury233 committed
1959 1960 1961 1962 1963 1964
            client.server.write(buffer);
          }
          client.established = true;
          client.pre_establish_buffers = [];
        });
      }
nanahira's avatar
nanahira committed
1965
    }
mercury233's avatar
mercury233 committed
1966

nanahira's avatar
nanahira committed
1967
    disconnect(client, error) {
1968
      var index, left_name, len2, len3, m, n, player, ref2, ref3;
nanahira's avatar
nanahira committed
1969 1970 1971
      if (client.had_new_reconnection) {
        return;
      }
mercury233's avatar
mercury233 committed
1972
      if (client.is_post_watcher) {
nanahira's avatar
nanahira committed
1973
        ygopro.stoc_send_chat_to_room(this, `${client.name} \${quit_watch}` + (error ? `: ${error}` : ''));
mercury233's avatar
mercury233 committed
1974 1975 1976 1977
        index = _.indexOf(this.watchers, client);
        if (index !== -1) {
          this.watchers.splice(index, 1);
        }
nanahira's avatar
nanahira committed
1978
        //client.room = null
nanahira's avatar
nanahira committed
1979
        SERVER_kick(client.server);
mercury233's avatar
mercury233 committed
1980
      } else {
nanahira's avatar
nanahira committed
1981
        //log.info(client.name, @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN, @disconnector, @random_type, @players.length)
nanahira's avatar
nanahira committed
1982
        if (this.arena && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && this.disconnector !== 'server' && !this.arena_score_handled) {
1983 1984 1985 1986 1987 1988 1989
          if (settings.modules.arena_mode.punish_quit_before_match && this.players.length === 2 && !client.arena_quit_free) {
            ref2 = this.players;
            for (m = 0, len2 = ref2.length; m < len2; m++) {
              player = ref2[m];
              if (player.pos !== 7) {
                this.scores[player.name_vpass] = 0;
              }
1990
            }
1991
            this.scores[client.name_vpass] = -9;
1992 1993 1994 1995 1996 1997 1998 1999
          } else {
            ref3 = this.players;
            for (n = 0, len3 = ref3.length; n < len3; n++) {
              player = ref3[n];
              if (player.pos !== 7) {
                this.scores[player.name_vpass] = -5;
              }
            }
nanahira's avatar
nanahira committed
2000 2001 2002
            if (this.players.length === 2) {
              this.scores[client.name_vpass] = -9;
            }
nanahira's avatar
-5  
nanahira committed
2003
          }
nanahira's avatar
nanahira committed
2004
          this.arena_score_handled = true;
2005
        }
mercury233's avatar
mercury233 committed
2006 2007 2008 2009
        index = _.indexOf(this.players, client);
        if (index !== -1) {
          this.players.splice(index, 1);
        }
nanahira's avatar
nanahira committed
2010
        if (this.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && this.disconnector !== 'server' && client.pos < 4) {
2011
          this.finished = true;
nanahira's avatar
nanahira committed
2012
          if (!this.finished_by_death) {
2013
            this.scores[client.name_vpass] = -9;
nanahira's avatar
fix  
nanahira committed
2014
            if (this.random_type && !client.flee_free && (!settings.modules.reconnect.enabled || this.get_disconnected_count() === 0)) {
nanahira's avatar
nanahira committed
2015
              ROOM_ban_player(client.name, client.ip, "${random_ban_reason_flee}");
mercury233's avatar
mercury233 committed
2016 2017 2018
              if (settings.modules.random_duel.record_match_scores && this.random_type === 'M') {
                ROOM_player_flee(client.name_vpass);
              }
nanahira's avatar
nanahira committed
2019
            }
mercury233's avatar
mercury233 committed
2020
          }
mercury233's avatar
mercury233 committed
2021
        }
nanahira's avatar
nanahira committed
2022 2023
        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
2024
          ygopro.stoc_send_chat_to_room(this, `${left_name} \${left_game}` + (error ? `: ${error}` : ''));
nanahira's avatar
nanahira committed
2025
          if (!this.windbot && this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.http.websocket_roomlist) {
mercury233's avatar
mercury233 committed
2026 2027 2028
            roomlist.update(this);
          }
        } else {
nanahira's avatar
nanahira committed
2029
          //client.room = null
nanahira's avatar
nanahira committed
2030
          this.send_replays();
mercury233's avatar
mercury233 committed
2031
          this.process.kill();
nanahira's avatar
nanahira committed
2032 2033
          //client.room = null
          this.delete();
mercury233's avatar
mercury233 committed
2034
        }
nanahira's avatar
nanahira committed
2035
        if (!CLIENT_reconnect_unregister(client, false, true)) {
nanahira's avatar
nanahira committed
2036
          SERVER_kick(client.server);
nanahira's avatar
nanahira committed
2037
        }
mercury233's avatar
mercury233 committed
2038
      }
nanahira's avatar
nanahira committed
2039
    }
mercury233's avatar
mercury233 committed
2040

nanahira's avatar
nanahira committed
2041
    start_death() {
nanahira's avatar
nanahira committed
2042
      var oppo_pos, win_pos;
nanahira's avatar
nanahira committed
2043
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || this.death) {
nanahira's avatar
nanahira committed
2044 2045 2046
        return false;
      }
      oppo_pos = this.hostinfo.mode === 2 ? 2 : 1;
nanahira's avatar
nanahira committed
2047
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
nanahira's avatar
nanahira committed
2048 2049 2050 2051 2052 2053 2054
        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
2055
            ygopro.stoc_send_chat_to_room(this, "${death_start}", ygopro.constants.COLORS.BABYBLUE); // Extra duel started in siding
nanahira's avatar
nanahira committed
2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096
        }
      } 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
2097
    }
nanahira's avatar
nanahira committed
2098

nanahira's avatar
nanahira committed
2099
    cancel_death() {
nanahira's avatar
nanahira committed
2100
      if (this.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || !this.death) {
nanahira's avatar
nanahira committed
2101 2102 2103 2104 2105
        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
2106
    }
mercury233's avatar
mercury233 committed
2107

nanahira's avatar
nanahira committed
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145
    termiate() {
      if (this.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
        this.scores[this.dueling_players[0].name_vpass] = 0;
        this.scores[this.dueling_players[1].name_vpass] = 0;
      }
      this.kicked = true;
      this.send_replays();
      this.process.kill();
      return this.delete();
    }

    finish_recover(fail) {
      var buffer, len2, m, player, ref2, results;
      if (fail) {
        ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED);
        return this.termiate();
      } else {
        ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE);
        this.recovering = false;
        ref2 = this.get_playing_player();
        results = [];
        for (m = 0, len2 = ref2.length; m < len2; m++) {
          player = ref2[m];
          results.push((function() {
            var len3, n, ref3, results1;
            ref3 = this.recover_buffers[player.pos];
            results1 = [];
            for (n = 0, len3 = ref3.length; n < len3; n++) {
              buffer = ref3[n];
              results1.push(ygopro.stoc_send(player, "GAME_MSG", buffer));
            }
            return results1;
          }).call(this));
        }
        return results;
      }
    }

nanahira's avatar
nanahira committed
2146
  };
mercury233's avatar
mercury233 committed
2147

nanahira's avatar
nanahira committed
2148
  // 网络连接
神楽坂玲奈's avatar
神楽坂玲奈 committed
2149
  net.createServer(function(client) {
mercury233's avatar
mercury233 committed
2150 2151
    var connect_count, server;
    client.ip = client.remoteAddress;
nanahira's avatar
nanahira committed
2152
    client.is_local = client.ip && (client.ip.includes('127.0.0.1') || client.ip.includes(real_windbot_server_ip));
mercury233's avatar
mercury233 committed
2153
    connect_count = ROOM_connected_ip[client.ip] || 0;
2154
    if (!settings.modules.test_mode.no_connect_count_limit && !client.is_local) {
mercury233's avatar
mercury233 committed
2155 2156 2157
      connect_count++;
    }
    ROOM_connected_ip[client.ip] = connect_count;
nanahira's avatar
nanahira committed
2158 2159 2160
    //log.info "connect", client.ip, ROOM_connected_ip[client.ip]

    // server stand for the connection to ygopro server process
神楽坂玲奈's avatar
神楽坂玲奈 committed
2161 2162
    server = new net.Socket();
    client.server = server;
nanahira's avatar
nanahira committed
2163
    server.client = client;
nanahira's avatar
nanahira committed
2164 2165 2166
    client.setTimeout(2000); //连接前超时2秒
    
    // 释放处理
2167
    client.on('close', function(had_error) {
mercury233's avatar
mercury233 committed
2168
      var room;
nanahira's avatar
nanahira committed
2169
      //log.info "client closed", client.name, had_error
2170
      room = ROOM_all[client.rid];
mercury233's avatar
mercury233 committed
2171 2172 2173 2174 2175
      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
2176
      //log.info "disconnect", client.ip, ROOM_connected_ip[client.ip]
2177
      if (!client.closed) {
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2178
        client.closed = true;
nanahira's avatar
nanahira committed
2179 2180 2181
        if (settings.modules.heartbeat_detection.enabled) {
          CLIENT_heartbeat_unregister(client);
        }
2182
        if (room) {
nanahira's avatar
nanahira committed
2183
          if (!CLIENT_reconnect_register(client, client.rid)) {
nanahira's avatar
nanahira committed
2184 2185 2186
            room.disconnect(client);
          }
        } else if (!client.had_new_reconnection) {
nanahira's avatar
nanahira committed
2187
          SERVER_kick(client.server);
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2188
        }
mercury233's avatar
mercury233 committed
2189 2190
      }
    });
2191
    client.on('error', function(error) {
mercury233's avatar
mercury233 committed
2192
      var room;
nanahira's avatar
nanahira committed
2193
      //log.info "client error", client.name, error
2194
      room = ROOM_all[client.rid];
mercury233's avatar
mercury233 committed
2195 2196 2197 2198 2199
      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
2200
      //log.info "err disconnect", client.ip, ROOM_connected_ip[client.ip]
2201
      if (!client.closed) {
nanahira's avatar
nanahira committed
2202
        client.closed = true;
2203
        if (room) {
nanahira's avatar
nanahira committed
2204
          if (!CLIENT_reconnect_register(client, client.rid, error)) {
nanahira's avatar
nanahira committed
2205 2206 2207
            room.disconnect(client, error);
          }
        } else if (!client.had_new_reconnection) {
nanahira's avatar
nanahira committed
2208
          SERVER_kick(client.server);
2209
        }
神楽坂玲奈's avatar
神楽坂玲奈 committed
2210
      }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2211
    });
2212
    client.on('timeout', function() {
nanahira's avatar
nanahira committed
2213
      if (!(settings.modules.reconnect.enabled && (disconnect_list[CLIENT_get_authorize_key(client)] || client.had_new_reconnection))) {
nanahira's avatar
nanahira committed
2214
        client.destroy();
nanahira's avatar
nanahira committed
2215
      }
2216
    });
2217
    server.on('close', function(had_error) {
2218
      var room;
2219 2220
      if (!server.closed) {
        server.closed = true;
mercury233's avatar
mercury233 committed
2221
      }
nanahira's avatar
nanahira committed
2222 2223 2224
      if (!server.client) {
        return;
      }
nanahira's avatar
nanahira committed
2225
      //log.info "server closed", server.client.name, had_error
nanahira's avatar
nanahira committed
2226
      room = ROOM_all[server.client.rid];
nanahira's avatar
nanahira committed
2227
      if (room && !server.system_kicked && !server.had_new_reconnection) {
nanahira's avatar
nanahira committed
2228
        //log.info "server close", server.client.ip, ROOM_connected_ip[server.client.ip]
nanahira's avatar
nanahira committed
2229 2230
        room.disconnector = 'server';
      }
nanahira's avatar
rev  
nanahira committed
2231
      if (!server.client.closed) {
nanahira's avatar
nanahira committed
2232
        ygopro.stoc_send_chat(server.client, "${server_closed}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
2233 2234
        //if room and settings.modules.replay_delay
        //  room.send_replays()
nanahira's avatar
nanahira committed
2235 2236
        CLIENT_kick(server.client);
        SERVER_clear_disconnect(server);
mercury233's avatar
mercury233 committed
2237 2238
      }
    });
2239
    server.on('error', function(error) {
2240
      var room;
2241
      server.closed = error;
nanahira's avatar
nanahira committed
2242 2243 2244
      if (!server.client) {
        return;
      }
nanahira's avatar
nanahira committed
2245
      //log.info "server error", client.name, error
nanahira's avatar
nanahira committed
2246
      room = ROOM_all[server.client.rid];
nanahira's avatar
nanahira committed
2247
      if (room && !server.system_kicked && !server.had_new_reconnection) {
nanahira's avatar
nanahira committed
2248
        //log.info "server err close", client.ip, ROOM_connected_ip[client.ip]
nanahira's avatar
nanahira committed
2249 2250
        room.disconnector = 'server';
      }
nanahira's avatar
nanahira committed
2251
      if (!server.client.closed) {
nanahira's avatar
nanahira committed
2252 2253 2254
        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
2255 2256
        CLIENT_kick(server.client);
        SERVER_clear_disconnect(server);
mercury233's avatar
mercury233 committed
2257 2258
      }
    });
mercury233's avatar
mercury233 committed
2259
    if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
mercury233's avatar
mercury233 committed
2260
      log.info('BAD IP', client.ip);
nanahira's avatar
nanahira committed
2261
      CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2262 2263
      return;
    }
mercury233's avatar
mercury233 committed
2264
    if (settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
2265 2266 2267
      client.open_cloud_replay = function(err, replay) {
        var buffer;
        if (err || !replay) {
mercury233's avatar
mercury233 committed
2268
          ygopro.stoc_die(client, "${cloud_replay_no}");
mercury233's avatar
mercury233 committed
2269
          return;
mercury233's avatar
mercury233 committed
2270
        }
mercury233's avatar
mercury233 committed
2271
        redisdb.expire("replay:" + replay.replay_id, 60 * 60 * 48);
nanahira's avatar
nanahira committed
2272
        buffer = Buffer.from(replay.replay_buffer, 'binary');
mercury233's avatar
mercury233 committed
2273 2274
        zlib.unzip(buffer, function(err, replay_buffer) {
          if (err) {
mercury233's avatar
mercury233 committed
2275
            log.info("cloud replay unzip error: " + err);
mercury233's avatar
mercury233 committed
2276
            ygopro.stoc_send_chat(client, "${cloud_replay_error}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
2277
            CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2278 2279
            return;
          }
nanahira's avatar
nanahira committed
2280
          ygopro.stoc_send_chat(client, `\${cloud_replay_playing} R#${replay.replay_id} ${replay.player_names} ${replay.date_time}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
2281
          client.write(replay_buffer, function() {
nanahira's avatar
nanahira committed
2282
            CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2283
          });
mercury233's avatar
mercury233 committed
2284
        });
mercury233's avatar
mercury233 committed
2285 2286
      };
    }
nanahira's avatar
nanahira committed
2287 2288
    // 需要重构
    // 客户端到服务端(ctos)协议分析
神楽坂玲奈's avatar
神楽坂玲奈 committed
2289
    client.pre_establish_buffers = new Array();
mercury233's avatar
mercury233 committed
2290
    client.on('data', function(ctos_buffer) {
nanahira's avatar
nanahira committed
2291
      var b, bad_ip_count, buffer, cancel, ctos_event, ctos_message_length, ctos_proto, datas, info, len2, len3, len4, len5, looplimit, m, n, o, p, ref2, ref3, result, room, struct;
2292
      if (client.is_post_watcher) {
2293
        room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
2294
        if (room && !CLIENT_is_banned_by_mc(client)) {
mercury233's avatar
mercury233 committed
2295
          room.watcher.write(ctos_buffer);
2296
        }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2297
      } else {
nanahira's avatar
nanahira committed
2298
        //ctos_buffer = Buffer.alloc(0)
mercury233's avatar
mercury233 committed
2299 2300
        ctos_message_length = 0;
        ctos_proto = 0;
nanahira's avatar
nanahira committed
2301
        //ctos_buffer = Buffer.concat([ctos_buffer, data], ctos_buffer.length + data.length) #buffer的错误使用方式,好孩子不要学
2302
        datas = [];
mercury233's avatar
mercury233 committed
2303
        looplimit = 0;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2304
        while (true) {
2305 2306
          if (ctos_message_length === 0) {
            if (ctos_buffer.length >= 2) {
mercury233's avatar
test3  
mercury233 committed
2307
              ctos_message_length = ctos_buffer.readUInt16LE(0);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2308
            } else {
mercury233's avatar
mercury233 committed
2309 2310 2311
              if (ctos_buffer.length !== 0) {
                log.warn("bad ctos_buffer length", client.ip);
              }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2312 2313
              break;
            }
2314 2315
          } else if (ctos_proto === 0) {
            if (ctos_buffer.length >= 3) {
mercury233's avatar
test3  
mercury233 committed
2316
              ctos_proto = ctos_buffer.readUInt8(2);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2317
            } else {
mercury233's avatar
mercury233 committed
2318
              log.warn("bad ctos_proto length", client.ip);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2319
              break;
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2320 2321
            }
          } else {
2322
            if (ctos_buffer.length >= 2 + ctos_message_length) {
nanahira's avatar
nanahira committed
2323
              //console.log client.pos, "CTOS", ygopro.constants.CTOS[ctos_proto]
2324
              cancel = false;
nanahira's avatar
nanahira committed
2325 2326 2327
              if (settings.modules.reconnect.enabled && client.pre_reconnecting && ygopro.constants.CTOS[ctos_proto] !== 'UPDATE_DECK') {
                cancel = true;
              }
nanahira's avatar
nanahira committed
2328 2329
              b = ctos_buffer.slice(3, ctos_message_length - 1 + 3);
              info = null;
nanahira's avatar
nanahira committed
2330 2331
              struct = ygopro.structs[ygopro.proto_structs.CTOS[ygopro.constants.CTOS[ctos_proto]]];
              if (struct && !cancel) {
nanahira's avatar
nanahira committed
2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344
                struct._setBuff(b);
                info = _.clone(struct.fields);
              }
              if (ygopro.ctos_follows_before[ctos_proto] && !cancel) {
                ref2 = ygopro.ctos_follows_before[ctos_proto];
                for (m = 0, len2 = ref2.length; m < len2; m++) {
                  ctos_event = ref2[m];
                  result = ctos_event.callback(b, info, client, client.server, datas);
                  if (result && ctos_event.synchronous) {
                    cancel = true;
                  }
                }
              }
nanahira's avatar
nanahira committed
2345 2346 2347 2348
              if (struct && !cancel) {
                struct._setBuff(b);
                info = _.clone(struct.fields);
              }
nanahira's avatar
nanahira committed
2349
              if (ygopro.ctos_follows[ctos_proto] && !cancel) {
nanahira's avatar
nanahira committed
2350 2351 2352
                result = ygopro.ctos_follows[ctos_proto].callback(b, info, client, client.server, datas);
                if (result && ygopro.ctos_follows[ctos_proto].synchronous) {
                  cancel = true;
2353
                }
nanahira's avatar
nanahira committed
2354
              }
nanahira's avatar
nanahira committed
2355 2356 2357 2358
              if (struct && !cancel) {
                struct._setBuff(b);
                info = _.clone(struct.fields);
              }
nanahira's avatar
nanahira committed
2359 2360 2361 2362 2363 2364 2365 2366
              if (ygopro.ctos_follows_after[ctos_proto] && !cancel) {
                ref3 = ygopro.ctos_follows_after[ctos_proto];
                for (n = 0, len3 = ref3.length; n < len3; n++) {
                  ctos_event = ref3[n];
                  result = ctos_event.callback(b, info, client, client.server, datas);
                  if (result && ctos_event.synchronous) {
                    cancel = true;
                  }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2367 2368
                }
              }
2369 2370 2371
              if (!cancel) {
                datas.push(ctos_buffer.slice(0, 2 + ctos_message_length));
              }
2372 2373
              ctos_buffer = ctos_buffer.slice(2 + ctos_message_length);
              ctos_message_length = 0;
mercury233's avatar
test3  
mercury233 committed
2374
              ctos_proto = 0;
mercury233's avatar
fix  
mercury233 committed
2375 2376 2377 2378
            } else {
              if (ctos_message_length !== 17735) {
                log.warn("bad ctos_message length", client.ip, ctos_buffer.length, ctos_message_length, ctos_proto);
              }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
2379 2380
              break;
            }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2381
          }
mercury233's avatar
mercury233 committed
2382
          looplimit++;
nanahira's avatar
nanahira committed
2383
          //log.info(looplimit)
mercury233's avatar
mercury233 committed
2384 2385 2386
          if (looplimit > 800 || ROOM_bad_ip[client.ip] > 5) {
            log.info("error ctos", client.name, client.ip);
            bad_ip_count = ROOM_bad_ip[client.ip];
mercury233's avatar
mercury233 committed
2387
            if (bad_ip_count) {
mercury233's avatar
mercury233 committed
2388
              ROOM_bad_ip[client.ip] = bad_ip_count + 1;
mercury233's avatar
mercury233 committed
2389
            } else {
mercury233's avatar
mercury233 committed
2390
              ROOM_bad_ip[client.ip] = 1;
mercury233's avatar
mercury233 committed
2391
            }
nanahira's avatar
nanahira committed
2392
            CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2393 2394
            break;
          }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2395
        }
nanahira's avatar
nanahira committed
2396 2397 2398
        if (!client.server) {
          return;
        }
2399
        if (client.established) {
nanahira's avatar
nanahira committed
2400 2401
          for (o = 0, len4 = datas.length; o < len4; o++) {
            buffer = datas[o];
nanahira's avatar
nanahira committed
2402
            client.server.write(buffer);
2403 2404
          }
        } else {
nanahira's avatar
nanahira committed
2405 2406
          for (p = 0, len5 = datas.length; p < len5; p++) {
            buffer = datas[p];
2407 2408 2409
            client.pre_establish_buffers.push(buffer);
          }
        }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2410 2411
      }
    });
nanahira's avatar
nanahira committed
2412
    // 服务端到客户端(stoc)
mercury233's avatar
mercury233 committed
2413
    server.on('data', function(stoc_buffer) {
nanahira's avatar
nanahira committed
2414
      var b, buffer, cancel, datas, info, len2, len3, len4, looplimit, m, n, o, ref2, ref3, result, stoc_event, stoc_message_length, stoc_proto, struct;
nanahira's avatar
nanahira committed
2415
      //stoc_buffer = Buffer.alloc(0)
mercury233's avatar
mercury233 committed
2416 2417
      stoc_message_length = 0;
      stoc_proto = 0;
nanahira's avatar
nanahira committed
2418 2419 2420 2421
      //stoc_buffer = Buffer.concat([stoc_buffer, data], stoc_buffer.length + data.length) #buffer的错误使用方式,好孩子不要学

      //unless ygopro.stoc_follows[stoc_proto] and ygopro.stoc_follows[stoc_proto].synchronous
      //server.client.write data
2422
      datas = [];
mercury233's avatar
mercury233 committed
2423
      looplimit = 0;
神楽坂玲奈's avatar
神楽坂玲奈 committed
2424
      while (true) {
2425 2426
        if (stoc_message_length === 0) {
          if (stoc_buffer.length >= 2) {
mercury233's avatar
test3  
mercury233 committed
2427
            stoc_message_length = stoc_buffer.readUInt16LE(0);
神楽坂玲奈's avatar
神楽坂玲奈 committed
2428
          } else {
mercury233's avatar
mercury233 committed
2429
            if (stoc_buffer.length !== 0) {
nanahira's avatar
nanahira committed
2430
              log.warn("bad stoc_buffer length", server.client.ip);
mercury233's avatar
mercury233 committed
2431
            }
神楽坂玲奈's avatar
神楽坂玲奈 committed
2432 2433
            break;
          }
2434 2435
        } else if (stoc_proto === 0) {
          if (stoc_buffer.length >= 3) {
mercury233's avatar
test3  
mercury233 committed
2436
            stoc_proto = stoc_buffer.readUInt8(2);
神楽坂玲奈's avatar
神楽坂玲奈 committed
2437
          } else {
nanahira's avatar
nanahira committed
2438
            log.warn("bad stoc_proto length", server.client.ip);
神楽坂玲奈's avatar
神楽坂玲奈 committed
2439 2440 2441
            break;
          }
        } else {
2442
          if (stoc_buffer.length >= 2 + stoc_message_length) {
nanahira's avatar
nanahira committed
2443
            //console.log client.pos, "STOC", ygopro.constants.STOC[stoc_proto]
2444
            cancel = false;
nanahira's avatar
nanahira committed
2445 2446
            b = stoc_buffer.slice(3, stoc_message_length - 1 + 3);
            info = null;
nanahira's avatar
nanahira committed
2447 2448
            struct = ygopro.structs[ygopro.proto_structs.STOC[ygopro.constants.STOC[stoc_proto]]];
            if (struct && !cancel) {
nanahira's avatar
nanahira committed
2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459
              struct._setBuff(b);
              info = _.clone(struct.fields);
            }
            if (ygopro.stoc_follows_before[stoc_proto] && !cancel) {
              ref2 = ygopro.stoc_follows_before[stoc_proto];
              for (m = 0, len2 = ref2.length; m < len2; m++) {
                stoc_event = ref2[m];
                result = stoc_event.callback(b, info, server.client, server, datas);
                if (result && stoc_event.synchronous) {
                  cancel = true;
                }
2460
              }
nanahira's avatar
nanahira committed
2461
            }
nanahira's avatar
nanahira committed
2462 2463 2464 2465
            if (struct && !cancel) {
              struct._setBuff(b);
              info = _.clone(struct.fields);
            }
nanahira's avatar
nanahira committed
2466 2467 2468 2469 2470 2471
            if (ygopro.stoc_follows[stoc_proto] && !cancel) {
              result = ygopro.stoc_follows[stoc_proto].callback(b, info, server.client, server, datas);
              if (result && ygopro.stoc_follows[stoc_proto].synchronous) {
                cancel = true;
              }
            }
nanahira's avatar
nanahira committed
2472 2473 2474 2475
            if (struct && !cancel) {
              struct._setBuff(b);
              info = _.clone(struct.fields);
            }
nanahira's avatar
nanahira committed
2476 2477 2478 2479 2480 2481 2482 2483
            if (ygopro.stoc_follows_after[stoc_proto] && !cancel) {
              ref3 = ygopro.stoc_follows_after[stoc_proto];
              for (n = 0, len3 = ref3.length; n < len3; n++) {
                stoc_event = ref3[n];
                result = stoc_event.callback(b, info, server.client, server, datas);
                if (result && stoc_event.synchronous) {
                  cancel = true;
                }
神楽坂玲奈's avatar
神楽坂玲奈 committed
2484 2485
              }
            }
2486 2487 2488
            if (!cancel) {
              datas.push(stoc_buffer.slice(0, 2 + stoc_message_length));
            }
2489 2490
            stoc_buffer = stoc_buffer.slice(2 + stoc_message_length);
            stoc_message_length = 0;
mercury233's avatar
test3  
mercury233 committed
2491
            stoc_proto = 0;
神楽坂玲奈's avatar
神楽坂玲奈 committed
2492
          } else {
nanahira's avatar
nanahira committed
2493
            log.warn("bad stoc_message length", server.client.ip);
神楽坂玲奈's avatar
神楽坂玲奈 committed
2494 2495 2496
            break;
          }
        }
mercury233's avatar
mercury233 committed
2497
        looplimit++;
nanahira's avatar
nanahira committed
2498
        //log.info(looplimit)
mercury233's avatar
mercury233 committed
2499
        if (looplimit > 800) {
nanahira's avatar
nanahira committed
2500
          log.info("error stoc", server.client.name);
2501
          server.destroy();
mercury233's avatar
mercury233 committed
2502 2503
          break;
        }
神楽坂玲奈's avatar
 
神楽坂玲奈 committed
2504
      }
nanahira's avatar
nanahira committed
2505
      if (server.client && !server.client.closed) {
nanahira's avatar
nanahira committed
2506 2507
        for (o = 0, len4 = datas.length; o < len4; o++) {
          buffer = datas[o];
nanahira's avatar
nanahira committed
2508 2509
          server.client.write(buffer);
        }
2510
      }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2511
    });
神楽坂玲奈's avatar
神楽坂玲奈 committed
2512
  }).listen(settings.port, function() {
mercury233's avatar
mercury233 committed
2513
    log.info("server started", settings.port);
神楽坂玲奈's avatar
神楽坂玲奈 committed
2514
  });
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2515

mercury233's avatar
mercury233 committed
2516 2517 2518 2519
  if (settings.modules.stop) {
    log.info("NOTE: server not open due to config, ", settings.modules.stop);
  }

Yuzurisa's avatar
Yuzurisa committed
2520 2521 2522 2523 2524
  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
2525
    parsed_deck_name = deck_name.match(/^([^\+ \uff0b]+)[\+ \uff0b](.+?)(\.ydk){0,2}$/);
Yuzurisa's avatar
Yuzurisa committed
2526 2527 2528
    return parsed_deck_name && (player_name === parsed_deck_name[1] || player_name === parsed_deck_name[2]);
  };

nanahira's avatar
nanahira committed
2529 2530
  // 功能模块
  // return true to cancel a synchronous message
nanahira's avatar
nanahira committed
2531
  ygopro.ctos_follow('PLAYER_INFO', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
2532
    var geo, lang, name, name_full, struct, vpass;
nanahira's avatar
nanahira committed
2533 2534
    // checkmate use username$password, but here don't
    // so remove the password
nanahira's avatar
nanahira committed
2535 2536 2537 2538 2539 2540
    name_full = info.name.split("$");
    name = name_full[0];
    vpass = name_full[1];
    if (vpass && !vpass.length) {
      vpass = null;
    }
mercury233's avatar
mercury233 committed
2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552
    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;
    }
nanahira's avatar
typo  
nanahira committed
2553
    if (settings.modules.mycard.enabled) {
nanahira's avatar
nanahira committed
2554
      //console.log(name)
nanahira's avatar
nanahira committed
2555 2556 2557 2558 2559 2560 2561
      request({
        url: settings.modules.mycard.ban_get,
        json: true,
        qs: {
          user: name
        }
      }, function(error, response, body) {
nanahira's avatar
nanahira committed
2562
        //console.log(body)
nanahira's avatar
nanahira committed
2563 2564 2565 2566 2567 2568 2569 2570 2571
        if (_.isString(body)) {
          log.warn("ban get bad json", body);
        } else if (error || !body) {
          log.warn('ban get error', error, response);
        } else {
          client.ban_mc = body;
        }
      });
    }
2572 2573 2574 2575 2576
    struct = ygopro.structs["CTOS_PlayerInfo"];
    struct._setBuff(buffer);
    struct.set("name", name);
    buffer = struct.buffer;
    client.name = name;
nanahira's avatar
nanahira committed
2577
    client.vpass = vpass;
2578
    client.name_vpass = vpass ? name + "$" + vpass : name;
2579
    if (!settings.modules.i18n.auto_pick || client.is_local) {
nanahira's avatar
nanahira committed
2580
      client.lang = settings.modules.i18n.default;
mercury233's avatar
mercury233 committed
2581 2582 2583
    } else {
      geo = geoip.lookup(client.ip);
      if (!geo) {
mercury233's avatar
mercury233 committed
2584
        log.warn("fail to locate ip", client.name, client.ip);
mercury233's avatar
mercury233 committed
2585 2586 2587 2588 2589
        client.lang = settings.modules.i18n.fallback;
      } else {
        if (lang = settings.modules.i18n.map[geo.country]) {
          client.lang = lang;
        } else {
nanahira's avatar
nanahira committed
2590
          //log.info("Not in map", geo.country, client.name, client.ip)
mercury233's avatar
mercury233 committed
2591 2592 2593
          client.lang = settings.modules.i18n.fallback;
        }
      }
mercury233's avatar
mercury233 committed
2594
    }
2595
    return false;
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
2596 2597
  });

nanahira's avatar
nanahira committed
2598
  ygopro.ctos_follow('JOIN_GAME', false, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
2599
    var available_logs, check_buffer_indentity, create_room_with_action, len2, len3, m, n, name, pre_room, ref2, ref3, replay_id, room;
nanahira's avatar
nanahira committed
2600
    //log.info info
mercury233's avatar
mercury233 committed
2601
    info.pass = info.pass.trim();
nanahira's avatar
nanahira committed
2602 2603
    client.pass = info.pass;
    if (CLIENT_is_able_to_reconnect(client) || CLIENT_is_able_to_kick_reconnect(client)) {
nanahira's avatar
nanahira committed
2604 2605 2606
      CLIENT_pre_reconnect(client);
      return;
    } else if (settings.modules.stop) {
mercury233's avatar
mercury233 committed
2607
      ygopro.stoc_die(client, settings.modules.stop);
nanahira's avatar
nanahira committed
2608 2609
    } else if (info.pass === "Marshtomp" || info.pass === "the Big Brother") {
      ygopro.stoc_die(client, "${bad_user_name}");
mercury233's avatar
mercury233 committed
2610
    } else if (info.pass.toUpperCase() === "R" && settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
2611
      ygopro.stoc_send_chat(client, "${cloud_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
2612
      redisdb.lrange(CLIENT_get_authorize_key(client) + ":replays", 0, 2, function(err, result) {
mercury233's avatar
mercury233 committed
2613 2614 2615
        _.each(result, function(replay_id, id) {
          redisdb.hgetall("replay:" + replay_id, function(err, replay) {
            if (err || !replay) {
mercury233's avatar
mercury233 committed
2616 2617 2618
              if (err) {
                log.info("cloud replay getall error: " + err);
              }
mercury233's avatar
mercury233 committed
2619 2620
              return;
            }
nanahira's avatar
nanahira committed
2621
            ygopro.stoc_send_chat(client, `<${id - 0 + 1}> R#${replay_id} ${replay.player_names} ${replay.date_time}`, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
mercury233 committed
2622
          });
mercury233's avatar
mercury233 committed
2623 2624
        });
      });
nanahira's avatar
nanahira committed
2625
      // 强行等待异步执行完毕_(:з」∠)_
mercury233's avatar
mercury233 committed
2626 2627 2628
      setTimeout((function() {
        ygopro.stoc_send(client, 'ERROR_MSG', {
          msg: 1,
mercury233's avatar
mercury233 committed
2629
          code: 9
mercury233's avatar
mercury233 committed
2630
        });
nanahira's avatar
nanahira committed
2631
        CLIENT_kick(client);
mercury233's avatar
mercury233 committed
2632
      }), 500);
nanahira's avatar
nanahira committed
2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652
    } else if (info.pass.toUpperCase() === "RC" && settings.modules.tournament_mode.enable_recover) {
      ygopro.stoc_send_chat(client, "${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
      available_logs = duel_log.duel_log.filter(function(duel) {
        return duel.roommode !== 2 && _.any(duel.players, function(player) {
          return player.real_name === client.name_vpass;
        });
      }).slice(0, 8);
      _.each(available_logs, function(duel) {
        var player_names;
        player_names = duel.players[0].real_name + (duel.players[2] ? "+" + duel.players[2].real_name : "") + " VS " + (duel.players[1] ? duel.players[1].real_name : "AI") + (duel.players[3] ? "+" + duel.players[3].real_name : "");
        return ygopro.stoc_send_chat(client, `<${duel.id}> ${player_names} ${duel.time}`, ygopro.constants.COLORS.BABYBLUE);
      });
      // 强行等待异步执行完毕_(:з」∠)_
      setTimeout((function() {
        ygopro.stoc_send(client, 'ERROR_MSG', {
          msg: 1,
          code: 9
        });
        CLIENT_kick(client);
      }), 500);
mercury233's avatar
mercury233 committed
2653
    } else if (info.pass.slice(0, 2).toUpperCase() === "R#" && settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
2654
      replay_id = info.pass.split("#")[1];
mercury233's avatar
fix  
mercury233 committed
2655
      if (replay_id > 0 && replay_id <= 9) {
mercury233's avatar
mercury233 committed
2656
        redisdb.lindex(client.ip + ":replays", replay_id - 1, function(err, replay_id) {
mercury233's avatar
mercury233 committed
2657
          if (err || !replay_id) {
mercury233's avatar
mercury233 committed
2658 2659 2660
            if (err) {
              log.info("cloud replay replayid error: " + err);
            }
mercury233's avatar
mercury233 committed
2661
            ygopro.stoc_die(client, "${cloud_replay_no}");
mercury233's avatar
mercury233 committed
2662 2663 2664 2665
            return;
          }
          redisdb.hgetall("replay:" + replay_id, client.open_cloud_replay);
        });
mercury233's avatar
mercury233 committed
2666 2667 2668
      } else if (replay_id) {
        redisdb.hgetall("replay:" + replay_id, client.open_cloud_replay);
      } else {
mercury233's avatar
mercury233 committed
2669
        ygopro.stoc_die(client, "${cloud_replay_no}");
mercury233's avatar
mercury233 committed
2670
      }
mercury233's avatar
mercury233 committed
2671
    } else if (info.pass.toUpperCase() === "W" && settings.modules.cloud_replay.enabled) {
mercury233's avatar
mercury233 committed
2672 2673
      replay_id = Cloud_replay_ids[Math.floor(Math.random() * Cloud_replay_ids.length)];
      redisdb.hgetall("replay:" + replay_id, client.open_cloud_replay);
nanahira's avatar
nanahira committed
2674
    } else if (info.version !== settings.version) { // and (info.version < 9020 or settings.version != 4927) #强行兼容23333版
mercury233's avatar
mercury233 committed
2675
      ygopro.stoc_send_chat(client, settings.modules.update, ygopro.constants.COLORS.RED);
mercury233's avatar
fix  
mercury233 committed
2676 2677 2678 2679
      ygopro.stoc_send(client, 'ERROR_MSG', {
        msg: 4,
        code: settings.version
      });
nanahira's avatar
nanahira committed
2680
      CLIENT_kick(client);
nanahira's avatar
nanahira committed
2681
    } else if (!info.pass.length && !settings.modules.random_duel.enabled && !settings.modules.windbot.enabled && !settings.modules.challonge.enabled) {
mercury233's avatar
mercury233 committed
2682
      ygopro.stoc_die(client, "${blank_room_name}");
mercury233's avatar
fix  
mercury233 committed
2683
    } else if (info.pass.length && settings.modules.mycard.enabled && info.pass.slice(0, 3) !== 'AI#') {
mercury233's avatar
mercury233 committed
2684
      ygopro.stoc_send_chat(client, '${loading_user_info}', ygopro.constants.COLORS.BABYBLUE);
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2685
      if (info.pass.length <= 8) {
mercury233's avatar
mercury233 committed
2686
        ygopro.stoc_die(client, '${invalid_password_length}');
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2687 2688
        return;
      }
nanahira's avatar
nanahira committed
2689 2690 2691 2692 2693 2694
      //if info.version >= 9020 and settings.version == 4927 #强行兼容23333版
      //  info.version = settings.version
      //  struct = ygopro.structs["CTOS_JoinGame"]
      //  struct._setBuff(buffer)
      //  struct.set("version", info.version)
      //  buffer = struct.buffer
nanahira's avatar
nanahira committed
2695
      buffer = Buffer.from(info.pass.slice(0, 8), 'base64');
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2696
      if (buffer.length !== 6) {
mercury233's avatar
mercury233 committed
2697
        ygopro.stoc_die(client, '${invalid_password_payload}');
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2698 2699
        return;
      }
nanahira's avatar
nanahira committed
2700
      check_buffer_indentity = function(buf) {
nanahira's avatar
nanahira committed
2701
        var checksum, i, m, ref2;
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2702
        checksum = 0;
nanahira's avatar
nanahira committed
2703
        for (i = m = 0, ref2 = buf.length; (0 <= ref2 ? m < ref2 : m > ref2); i = 0 <= ref2 ? ++m : --m) {
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2704
          checksum += buf.readUInt8(i);
2705
        }
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2706 2707
        return (checksum & 0xFF) === 0;
      };
nanahira's avatar
nanahira committed
2708
      create_room_with_action = function(buffer, decrypted_buffer, match_permit) {
nanahira's avatar
nanahira committed
2709
        var action, len2, len3, m, n, name, opt1, opt2, opt3, options, player, ref2, ref3, room, room_title, title;
nanahira's avatar
nanahira committed
2710 2711 2712
        if (client.closed) {
          return;
        }
2713 2714
        action = buffer.readUInt8(1) >> 4;
        if (buffer !== decrypted_buffer && (action === 1 || action === 2 || action === 4)) {
mercury233's avatar
mercury233 committed
2715
          ygopro.stoc_die(client, '${invalid_password_unauthorized}');
2716 2717
          return;
        }
nanahira's avatar
nanahira committed
2718 2719 2720 2721 2722
        // 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
2723 2724 2725 2726
        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
2727
            if (ROOM_find_by_name(name)) {
mercury233's avatar
mercury233 committed
2728
              ygopro.stoc_die(client, '${invalid_password_existed}');
2729 2730 2731
              return;
            }
            opt1 = buffer.readUInt8(2);
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2732
            opt2 = buffer.readUInt16LE(3);
2733 2734 2735 2736 2737 2738
            opt3 = buffer.readUInt8(5);
            options = {
              lflist: 0,
              time_limit: 180,
              rule: (opt1 >> 5) & 3,
              mode: (opt1 >> 3) & 3,
nanahira's avatar
nanahira committed
2739
              duel_rule: (!!((opt1 >> 2) & 1) ? 4 : 5),
2740 2741 2742 2743
              no_check_deck: !!((opt1 >> 1) & 1),
              no_shuffle_deck: !!(opt1 & 1),
              start_lp: opt2,
              start_hand: opt3 >> 4,
nanahira's avatar
nanahira committed
2744 2745 2746
              draw_count: opt3 & 0xF,
              no_watch: false,
              auto_death: false
2747
            };
mercury233's avatar
mercury233 committed
2748
            options.lflist = _.findIndex(lflists, function(list) {
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2749
              return ((options.rule === 1) === list.tcg) && list.date.isBefore();
神楽坂玲奈's avatar
神楽坂玲奈 committed
2750
            });
nanahira's avatar
nanahira committed
2751 2752 2753 2754 2755 2756
            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
2757
              log.warn("BAD ROOM NAME LEVEL 3", room_title, client.name, client.ip);
nanahira's avatar
nanahira committed
2758
              ygopro.stoc_die(client, "${bad_roomname_level3}");
nanahira's avatar
nanahira committed
2759 2760 2761 2762 2763 2764
              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
2765
              log.warn("BAD ROOM NAME LEVEL 2", room_title, client.name, client.ip);
nanahira's avatar
nanahira committed
2766
              ygopro.stoc_die(client, "${bad_roomname_level2}");
nanahira's avatar
nanahira committed
2767 2768 2769 2770 2771 2772
              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
2773
              log.warn("BAD ROOM NAME LEVEL 1", room_title, client.name, client.ip);
nanahira's avatar
nanahira committed
2774
              ygopro.stoc_die(client, "${bad_roomname_level1}");
nanahira's avatar
nanahira committed
2775 2776
              return;
            }
2777
            room = new Room(name, options);
nanahira's avatar
nanahira committed
2778
            if (room) {
nanahira's avatar
nanahira committed
2779
              room.title = room_title;
nanahira's avatar
nanahira committed
2780
              room.private = action === 2;
nanahira's avatar
nanahira committed
2781
            }
2782 2783 2784
            break;
          case 3:
            name = info.pass.slice(8);
mercury233's avatar
mercury233 committed
2785
            room = ROOM_find_by_name(name);
2786
            if (!room) {
mercury233's avatar
mercury233 committed
2787
              ygopro.stoc_die(client, '${invalid_password_not_found}');
2788 2789 2790 2791
              return;
            }
            break;
          case 4:
nanahira's avatar
move  
nanahira committed
2792 2793 2794 2795
            if (match_permit && !match_permit.permit) {
              ygopro.stoc_die(client, '${invalid_password_unauthorized}');
              return;
            }
mercury233's avatar
mercury233 committed
2796
            room = ROOM_find_or_create_by_name('M#' + info.pass.slice(8));
nanahira's avatar
nanahira committed
2797
            if (room) {
nanahira's avatar
nanahira committed
2798 2799 2800 2801 2802 2803 2804 2805 2806
              ref2 = room.get_playing_player();
              for (m = 0, len2 = ref2.length; m < len2; m++) {
                player = ref2[m];
                if (!(player && player.name === client.name)) {
                  continue;
                }
                ygopro.stoc_die(client, '${invalid_password_unauthorized}');
                return;
              }
nanahira's avatar
nanahira committed
2807
              room.private = true;
nanahira's avatar
nanahira committed
2808 2809 2810 2811 2812
              room.arena = settings.modules.arena_mode.mode;
              if (room.arena === "athletic") {
                room.max_player = 2;
                room.welcome = "${athletic_arena_tip}";
              }
2813
            }
2814
            break;
mercury233's avatar
mercury233 committed
2815 2816 2817 2818 2819 2820 2821 2822
          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;
2823
          default:
mercury233's avatar
mercury233 committed
2824
            ygopro.stoc_die(client, '${invalid_password_action}');
2825 2826
            return;
        }
mercury233's avatar
mercury233 committed
2827
        if (!room) {
mercury233's avatar
mercury233 committed
2828
          ygopro.stoc_die(client, "${server_full}");
mercury233's avatar
mercury233 committed
2829
        } else if (room.error) {
mercury233's avatar
mercury233 committed
2830
          ygopro.stoc_die(client, room.error);
nanahira's avatar
nanahira committed
2831
        } else if (room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
nanahira's avatar
nanahira committed
2832
          if (settings.modules.cloud_replay.enable_halfway_watch && !room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
2833
            client.setTimeout(300000); //连接后超时5分钟
mercury233's avatar
mercury233 committed
2834 2835
            client.rid = _.indexOf(ROOM_all, room);
            client.is_post_watcher = true;
nanahira's avatar
nanahira committed
2836
            ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
mercury233's avatar
mercury233 committed
2837 2838
            room.watchers.push(client);
            ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
2839 2840 2841
            ref3 = room.watcher_buffers;
            for (n = 0, len3 = ref3.length; n < len3; n++) {
              buffer = ref3[n];
mercury233's avatar
mercury233 committed
2842 2843 2844 2845 2846
              client.write(buffer);
            }
          } else {
            ygopro.stoc_die(client, "${watch_denied}");
          }
nanahira's avatar
nanahira committed
2847
        } else if (room.hostinfo.no_watch && room.players.length >= (room.hostinfo.mode === 2 ? 4 : 2)) {
nanahira's avatar
fix  
nanahira committed
2848
          ygopro.stoc_die(client, "${watch_denied_room}");
mercury233's avatar
mercury233 committed
2849
        } else {
nanahira's avatar
nanahira committed
2850 2851
          //client.room = room
          client.setTimeout(300000); //连接后超时5分钟
2852 2853
          client.rid = _.indexOf(ROOM_all, room);
          room.connect(client);
mercury233's avatar
mercury233 committed
2854
        }
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2855
      };
nanahira's avatar
nanahira committed
2856 2857
      _async.auto({
        match_permit: function(done) {
nanahira's avatar
fix  
nanahira committed
2858 2859 2860 2861
          if (client.closed) {
            done();
            return;
          }
nanahira's avatar
nanahira committed
2862 2863 2864
          if (!settings.modules.arena_mode.check_permit) {
            done(null, null);
            return;
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2865
          }
nanahira's avatar
nanahira committed
2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879
          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
2880
              done(null, body);
nanahira's avatar
nanahira committed
2881 2882
            } else {
              log.warn("Match permit request error", error);
nanahira's avatar
fix  
nanahira committed
2883
              done(null, null);
nanahira's avatar
nanahira committed
2884 2885 2886 2887 2888 2889
            }
          });
        },
        get_user: function(done) {
          var decrypted_buffer, i, id, len2, m, ref2, secret;
          if (client.closed) {
nanahira's avatar
fix  
nanahira committed
2890
            done();
nanahira's avatar
nanahira committed
2891
            return;
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
2892
          }
nanahira's avatar
nanahira committed
2893 2894
          if (id = users_cache[client.name]) {
            secret = id % 65535 + 1;
nanahira's avatar
nanahira committed
2895
            decrypted_buffer = Buffer.allocUnsafe(6);
nanahira's avatar
nanahira committed
2896 2897 2898
            ref2 = [0, 2, 4];
            for (m = 0, len2 = ref2.length; m < len2; m++) {
              i = ref2[m];
nanahira's avatar
nanahira committed
2899 2900 2901
              decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i);
            }
            if (check_buffer_indentity(decrypted_buffer)) {
nanahira's avatar
nanahira committed
2902 2903 2904 2905
              done(null, {
                original: decrypted_buffer,
                decrypted: decrypted_buffer
              });
nanahira's avatar
fix  
nanahira committed
2906
              return;
nanahira's avatar
nanahira committed
2907 2908
            }
          }
nanahira's avatar
nanahira committed
2909
          //TODO: query database directly, like preload.
nanahira's avatar
nanahira committed
2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941
          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) {
            var len3, n, ref3;
            if (!error && body && body.user) {
              users_cache[client.name] = body.user.id;
              secret = body.user.id % 65535 + 1;
              decrypted_buffer = Buffer.allocUnsafe(6);
              ref3 = [0, 2, 4];
              for (n = 0, len3 = ref3.length; n < len3; n++) {
                i = ref3[n];
                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
2942
            done(null, {
nanahira's avatar
nanahira committed
2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955
              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
2956
        return create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit);
nanahira's avatar
nanahira committed
2957
      });
nanahira's avatar
nanahira committed
2958 2959
    } else if (settings.modules.challonge.enabled) {
      pre_room = ROOM_find_by_name(info.pass);
nanahira's avatar
nanahira committed
2960
      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
2961
        room = pre_room;
nanahira's avatar
nanahira committed
2962
        client.setTimeout(300000); //连接后超时5分钟
nanahira's avatar
nanahira committed
2963 2964
        client.rid = _.indexOf(ROOM_all, room);
        client.is_post_watcher = true;
nanahira's avatar
nanahira committed
2965
        ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
nanahira's avatar
nanahira committed
2966 2967
        room.watchers.push(client);
        ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
2968 2969 2970
        ref2 = room.watcher_buffers;
        for (m = 0, len2 = ref2.length; m < len2; m++) {
          buffer = ref2[m];
nanahira's avatar
nanahira committed
2971 2972 2973 2974
          client.write(buffer);
        }
      } else {
        ygopro.stoc_send_chat(client, '${loading_user_info}', ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
2975
        client.setTimeout(300000); //连接后超时5分钟
nanahira's avatar
nanahira committed
2976 2977
        _async.auto({
          participant_data: function(done) {
nanahira's avatar
nanahira committed
2978 2979 2980 2981
            challonge.participants._index({
              id: settings.modules.challonge.tournament_id,
              callback: done
            });
nanahira's avatar
nanahira committed
2982 2983
          },
          match_data: function(done) {
nanahira's avatar
nanahira committed
2984 2985 2986 2987 2988
            challonge.matches._index({
              id: settings.modules.challonge.tournament_id,
              callback: done
            });
          }
nanahira's avatar
nanahira committed
2989 2990
        }, function(err, datas) {
          var found, k, len3, len4, match, n, o, player, ref3, ref4, ref5, ref6, user;
nanahira's avatar
nanahira committed
2991 2992 2993
          if (client.closed) {
            return;
          }
nanahira's avatar
nanahira committed
2994
          if (err || !datas.participant_data || !datas.match_data) {
nanahira's avatar
nanahira committed
2995 2996 2997 2998 2999
            log.warn("Failed loading Challonge user info", err);
            ygopro.stoc_die(client, '${challonge_match_load_failed}');
            return;
          }
          found = false;
nanahira's avatar
nanahira committed
3000 3001 3002
          ref3 = datas.participant_data;
          for (k in ref3) {
            user = ref3[k];
nanahira's avatar
nanahira committed
3003 3004 3005
            if (user.participant && user.participant.name && deck_name_match(user.participant.name, client.name)) {
              found = user.participant;
              break;
nanahira's avatar
nanahira committed
3006
            }
nanahira's avatar
nanahira committed
3007 3008 3009 3010 3011 3012 3013
          }
          if (!found) {
            ygopro.stoc_die(client, '${challonge_user_not_found}');
            return;
          }
          client.challonge_info = found;
          found = false;
nanahira's avatar
nanahira committed
3014 3015 3016
          ref4 = datas.match_data;
          for (k in ref4) {
            match = ref4[k];
nanahira's avatar
nanahira committed
3017 3018 3019
            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
3020
            }
nanahira's avatar
nanahira committed
3021 3022 3023 3024 3025
          }
          if (!found) {
            ygopro.stoc_die(client, '${challonge_match_not_found}');
            return;
          }
nanahira's avatar
nanahira committed
3026 3027 3028
          //if found.winnerId
          //  ygopro.stoc_die(client, '${challonge_match_already_finished}')
          //  return
nanahira's avatar
nanahira committed
3029 3030 3031
          room = ROOM_find_or_create_by_name('M#' + found.id);
          if (room) {
            room.challonge_info = found;
nanahira's avatar
nanahira committed
3032
            // room.max_player = 2
nanahira's avatar
nanahira committed
3033 3034 3035 3036 3037 3038 3039 3040
            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
3041
              //client.setTimeout(300000) #连接后超时5分钟
nanahira's avatar
nanahira committed
3042 3043
              client.rid = _.indexOf(ROOM_all, room);
              client.is_post_watcher = true;
nanahira's avatar
nanahira committed
3044
              ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
nanahira's avatar
nanahira committed
3045 3046
              room.watchers.push(client);
              ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3047 3048 3049
              ref5 = room.watcher_buffers;
              for (n = 0, len3 = ref5.length; n < len3; n++) {
                buffer = ref5[n];
nanahira's avatar
nanahira committed
3050
                client.write(buffer);
nanahira's avatar
nanahira committed
3051
              }
nanahira's avatar
nanahira committed
3052 3053
            } else {
              ygopro.stoc_die(client, "${watch_denied}");
nanahira's avatar
nanahira committed
3054
            }
nanahira's avatar
nanahira committed
3055 3056 3057
          } 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
3058 3059 3060
            ref6 = room.get_playing_player();
            for (o = 0, len4 = ref6.length; o < len4; o++) {
              player = ref6[o];
nanahira's avatar
nanahira committed
3061 3062 3063 3064
              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
3065 3066
              return;
            }
nanahira's avatar
nanahira committed
3067 3068
            //client.room = room
            //client.setTimeout(300000) #连接后超时5分钟
nanahira's avatar
nanahira committed
3069 3070
            client.rid = _.indexOf(ROOM_all, room);
            room.connect(client);
nanahira's avatar
nanahira committed
3071 3072 3073
          }
        });
      }
mercury233's avatar
mercury233 committed
3074
    } else if (!client.name || client.name === "") {
mercury233's avatar
mercury233 committed
3075
      ygopro.stoc_die(client, "${bad_user_name}");
mercury233's avatar
mercury233 committed
3076 3077
    } else if (ROOM_connected_ip[client.ip] > 5) {
      log.warn("MULTI LOGIN", client.name, client.ip);
mercury233's avatar
mercury233 committed
3078
      ygopro.stoc_die(client, "${too_much_connection}" + client.ip);
nanahira's avatar
nanahira committed
3079
    } else if (_.indexOf(settings.ban.banned_user, client.name) > -1) { //账号被封
mercury233's avatar
mercury233 committed
3080
      settings.ban.banned_ip.push(client.ip);
mercury233's avatar
mercury233 committed
3081
      setting_save(settings);
mercury233's avatar
mercury233 committed
3082
      log.warn("BANNED USER LOGIN", client.name, client.ip);
mercury233's avatar
mercury233 committed
3083
      ygopro.stoc_die(client, "${banned_user_login}");
nanahira's avatar
nanahira committed
3084
    } else if (_.indexOf(settings.ban.banned_ip, client.ip) > -1) { //IP被封
mercury233's avatar
mercury233 committed
3085
      log.warn("BANNED IP LOGIN", client.name, client.ip);
mercury233's avatar
mercury233 committed
3086
      ygopro.stoc_die(client, "${banned_ip_login}");
3087
    } else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && _.any(badwords.level3, function(badword) {
mercury233's avatar
mercury233 committed
3088
      var regexp;
mercury233's avatar
fix  
mercury233 committed
3089
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
3090 3091
      return name.match(regexp);
    }, name = client.name)) {
mercury233's avatar
mercury233 committed
3092
      log.warn("BAD NAME LEVEL 3", client.name, client.ip);
mercury233's avatar
mercury233 committed
3093
      ygopro.stoc_die(client, "${bad_name_level3}");
3094
    } else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && _.any(badwords.level2, function(badword) {
mercury233's avatar
mercury233 committed
3095
      var regexp;
mercury233's avatar
fix  
mercury233 committed
3096
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
3097 3098
      return name.match(regexp);
    }, name = client.name)) {
mercury233's avatar
mercury233 committed
3099
      log.warn("BAD NAME LEVEL 2", client.name, client.ip);
mercury233's avatar
mercury233 committed
3100
      ygopro.stoc_die(client, "${bad_name_level2}");
3101
    } else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && _.any(badwords.level1, function(badword) {
mercury233's avatar
mercury233 committed
3102
      var regexp;
mercury233's avatar
fix  
mercury233 committed
3103
      regexp = new RegExp(badword, 'i');
mercury233's avatar
mercury233 committed
3104 3105
      return name.match(regexp);
    }, name = client.name)) {
mercury233's avatar
mercury233 committed
3106
      log.warn("BAD NAME LEVEL 1", client.name, client.ip);
mercury233's avatar
mercury233 committed
3107
      ygopro.stoc_die(client, "${bad_name_level1}");
mercury233's avatar
mercury233 committed
3108
    } else if (info.pass.length && !ROOM_validate(info.pass)) {
mercury233's avatar
mercury233 committed
3109
      ygopro.stoc_die(client, "${invalid_password_room}");
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
3110
    } else {
nanahira's avatar
nanahira committed
3111 3112 3113 3114 3115 3116 3117 3118 3119
      //if info.version >= 9020 and settings.version == 4927 #强行兼容23333版
      //  info.version = settings.version
      //  struct = ygopro.structs["CTOS_JoinGame"]
      //  struct._setBuff(buffer)
      //  struct.set("version", info.version)
      //  buffer = struct.buffer
      //  #ygopro.stoc_send_chat(client, "看起来你是YGOMobile的用户,请记得更新先行卡补丁,否则会看到白卡", ygopro.constants.COLORS.GREEN)

      //log.info 'join_game',info.pass, client.name
mercury233's avatar
mercury233 committed
3120
      room = ROOM_find_or_create_by_name(info.pass, client.ip);
mercury233's avatar
mercury233 committed
3121
      if (!room) {
mercury233's avatar
mercury233 committed
3122
        ygopro.stoc_die(client, "${server_full}");
mercury233's avatar
mercury233 committed
3123
      } else if (room.error) {
mercury233's avatar
mercury233 committed
3124
        ygopro.stoc_die(client, room.error);
nanahira's avatar
nanahira committed
3125
      } else if (room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN) {
nanahira's avatar
nanahira committed
3126
        if (settings.modules.cloud_replay.enable_halfway_watch && !room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
3127
          client.setTimeout(300000); //连接后超时5分钟
3128
          client.rid = _.indexOf(ROOM_all, room);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3129
          client.is_post_watcher = true;
nanahira's avatar
nanahira committed
3130
          ygopro.stoc_send_chat_to_room(room, `${client.name} \${watch_join}`);
3131
          room.watchers.push(client);
mercury233's avatar
mercury233 committed
3132
          ygopro.stoc_send_chat(client, "${watch_watching}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
3133 3134 3135
          ref3 = room.watcher_buffers;
          for (n = 0, len3 = ref3.length; n < len3; n++) {
            buffer = ref3[n];
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3136 3137 3138
            client.write(buffer);
          }
        } else {
mercury233's avatar
mercury233 committed
3139
          ygopro.stoc_die(client, "${watch_denied}");
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3140
        }
nanahira's avatar
nanahira committed
3141
      } else if (room.hostinfo.no_watch && room.players.length >= (room.hostinfo.mode === 2 ? 4 : 2)) {
nanahira's avatar
nanahira committed
3142
        ygopro.stoc_die(client, "${watch_denied_room}");
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3143
      } else {
nanahira's avatar
nanahira committed
3144
        client.setTimeout(300000); //连接后超时5分钟
3145 3146
        client.rid = _.indexOf(ROOM_all, room);
        room.connect(client);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3147
      }
神楽坂玲奈's avatar
init  
神楽坂玲奈 committed
3148 3149 3150
    }
  });

nanahira's avatar
nanahira committed
3151
  ygopro.stoc_follow('JOIN_GAME', false, function(buffer, info, client, server, datas) {
3152
    var len2, m, player, recorder, ref2, room, watcher;
nanahira's avatar
nanahira committed
3153
    //欢迎信息
3154
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3155
    if (!(room && !client.reconnecting)) {
3156 3157
      return;
    }
3158 3159 3160
    if (!room.join_game_buffer) {
      room.join_game_buffer = buffer;
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3161
    if (settings.modules.welcome) {
mercury233's avatar
mercury233 committed
3162
      ygopro.stoc_send_chat(client, settings.modules.welcome, ygopro.constants.COLORS.GREEN);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3163
    }
3164 3165
    if (room.welcome) {
      ygopro.stoc_send_chat(client, room.welcome, ygopro.constants.COLORS.BABYBLUE);
mercury233's avatar
add  
mercury233 committed
3166
    }
3167 3168 3169
    if (room.welcome2) {
      ygopro.stoc_send_chat(client, room.welcome2, ygopro.constants.COLORS.PINK);
    }
nanahira's avatar
nanahira committed
3170
    if (settings.modules.arena_mode.enabled && !client.is_local) { //and not client.score_shown
mercury233's avatar
mercury233 committed
3171 3172 3173 3174
      request({
        url: settings.modules.arena_mode.get_score + encodeURIComponent(client.name),
        json: true
      }, function(error, response, body) {
mercury233's avatar
merge  
mercury233 committed
3175
        var rank_txt;
mercury233's avatar
merge  
mercury233 committed
3176 3177 3178 3179
        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
3180
        } else {
nanahira's avatar
nanahira committed
3181
          //log.info 'LOAD SCORE', client.name, body
mercury233's avatar
mercury233 committed
3182
          rank_txt = body.arena_rank > 0 ? "${rank_arena}" + body.arena_rank : "${rank_blank}";
nanahira's avatar
nanahira committed
3183
          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
3184 3185 3186
        }
      });
    }
nanahira's avatar
nanahira committed
3187
    //client.score_shown = true
3188 3189 3190 3191 3192 3193 3194 3195 3196 3197
    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);
      ref2 = room.players;
      for (m = 0, len2 = ref2.length; m < len2; m++) {
        player = ref2[m];
        if (player.pos !== 7 && player !== client) {
          ygopro.stoc_send_chat(client, ROOM_player_get_score(player), ygopro.constants.COLORS.GREEN);
        }
      }
    }
3198 3199
    if (!room.recorder) {
      room.recorder = recorder = net.connect(room.port, function() {
mercury233's avatar
mercury233 committed
3200 3201 3202 3203 3204
        ygopro.ctos_send(recorder, 'PLAYER_INFO', {
          name: "Marshtomp"
        });
        ygopro.ctos_send(recorder, 'JOIN_GAME', {
          version: settings.version,
mercury233's avatar
mercury233 committed
3205
          pass: "Marshtomp"
mercury233's avatar
mercury233 committed
3206
        });
mercury233's avatar
mercury233 committed
3207
        ygopro.ctos_send(recorder, 'HS_TOOBSERVER');
mercury233's avatar
mercury233 committed
3208 3209
      });
      recorder.on('data', function(data) {
3210
        room = ROOM_all[client.rid];
mercury233's avatar
mercury233 committed
3211
        if (!(room && settings.modules.cloud_replay.enabled)) {
mercury233's avatar
mercury233 committed
3212 3213
          return;
        }
mercury233's avatar
mercury233 committed
3214
        room.recorder_buffers.push(data);
mercury233's avatar
mercury233 committed
3215 3216 3217
      });
      recorder.on('error', function(error) {});
    }
nanahira's avatar
nanahira committed
3218
    if (settings.modules.cloud_replay.enable_halfway_watch && !room.watcher && !room.hostinfo.no_watch) {
3219
      room.watcher = watcher = settings.modules.test_mode.watch_public_hand ? room.recorder : net.connect(room.port, function() {
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3220 3221 3222 3223 3224
        ygopro.ctos_send(watcher, 'PLAYER_INFO', {
          name: "the Big Brother"
        });
        ygopro.ctos_send(watcher, 'JOIN_GAME', {
          version: settings.version,
mercury233's avatar
mercury233 committed
3225
          pass: "the Big Brother"
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3226
        });
mercury233's avatar
mercury233 committed
3227
        ygopro.ctos_send(watcher, 'HS_TOOBSERVER');
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3228
      });
mercury233's avatar
fix  
mercury233 committed
3229
      watcher.on('data', function(data) {
3230
        var len3, n, ref3, w;
3231 3232
        room = ROOM_all[client.rid];
        if (!room) {
3233 3234
          return;
        }
3235
        room.watcher_buffers.push(data);
3236 3237 3238
        ref3 = room.watchers;
        for (n = 0, len3 = ref3.length; n < len3; n++) {
          w = ref3[n];
nanahira's avatar
nanahira committed
3239
          if (w) { //a WTF fix
mercury233's avatar
test3  
mercury233 committed
3240
            w.write(data);
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3241
          }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3242 3243
        }
      });
mercury233's avatar
mercury233 committed
3244 3245 3246
      watcher.on('error', function(error) {
        log.error("watcher error", error);
      });
神楽坂玲奈's avatar
神楽坂玲奈 committed
3247
    }
神楽坂玲奈's avatar
tip  
神楽坂玲奈 committed
3248
  });
神楽坂玲奈's avatar
神楽坂玲奈 committed
3249

nanahira's avatar
nanahira committed
3250
  // 登场台词
nanahira's avatar
nanahira committed
3251
  load_dialogues = global.load_dialogues = function(callback) {
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3252
    request({
mercury233's avatar
mercury233 committed
3253
      url: settings.modules.dialogues.get,
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3254 3255 3256
      json: true
    }, function(error, response, body) {
      if (_.isString(body)) {
mercury233's avatar
test3  
mercury233 committed
3257
        log.warn("dialogues bad json", body);
神楽坂玲奈's avatar
12.2  
神楽坂玲奈 committed
3258
      } else if (error || !body) {
mercury233's avatar
test3  
mercury233 committed
3259
        log.warn('dialogues error', error, response);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3260
      } else {
mercury233's avatar
mercury233 committed
3261 3262
        setting_change(dialogues, "dialogues", body);
        log.info("dialogues loaded", _.size(dialogues.dialogues));
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3263
      }
nanahira's avatar
nanahira committed
3264 3265 3266
      if (callback) {
        callback(error, body);
      }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3267
    });
mercury233's avatar
mercury233 committed
3268 3269
  };

mercury233's avatar
mercury233 committed
3270
  if (settings.modules.dialogues.get) {
mercury233's avatar
mercury233 committed
3271
    load_dialogues();
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3272 3273
  }

nanahira's avatar
nanahira committed
3274
  ygopro.stoc_follow('GAME_MSG', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3275
    var card, chain, check, count, cpos, deck_found, found, hint_type, i, id, len2, len3, len4, len5, limbo_found, line, loc, m, max_loop, msg, n, o, oppo_pos, p, phase, player, playertype, pos, ppos, q, reason, ref2, ref3, ref4, ref5, ref6, ref7, room, trigger_location, val, win_pos;
3276
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3277
    if (!(room && !client.reconnecting)) {
3278 3279
      return;
    }
神楽坂玲奈's avatar
神楽坂玲奈 committed
3280
    msg = buffer.readInt8(0);
nanahira's avatar
nanahira committed
3281 3282 3283 3284 3285
    //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
3286 3287 3288 3289 3290 3291 3292 3293 3294 3295
    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
3296
          CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
3297
          CLIENT_kick(client);
nanahira's avatar
nanahira committed
3298 3299 3300 3301 3302 3303 3304 3305
          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
3306 3307 3308
          if (client.last_hint_msg) {
            ygopro.stoc_send(client, 'GAME_MSG', client.last_hint_msg);
          }
nanahira's avatar
nanahira committed
3309 3310 3311 3312 3313
          ygopro.stoc_send(client, 'GAME_MSG', client.last_game_msg);
          return true;
        }
      } else {
        client.last_game_msg = buffer;
nanahira's avatar
nanahira committed
3314
        client.last_game_msg_title = ygopro.constants.MSG[msg];
nanahira's avatar
nanahira committed
3315
      }
nanahira's avatar
nanahira committed
3316
    // log.info(client.name, client.last_game_msg_title)
nanahira's avatar
nanahira committed
3317 3318 3319
    } 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
3320
    }
nanahira's avatar
nanahira committed
3321 3322
    // 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
3323 3324 3325 3326 3327 3328 3329 3330 3331 3332
      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();
      }
3333
    }
nanahira's avatar
nanahira committed
3334 3335 3336
    //log.info("#{ygopro.constants.MSG[msg]}等待#{room.waiting_for_player.name}")

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

nanahira's avatar
nanahira committed
3669
  //房间管理
nanahira's avatar
nanahira committed
3670
  ygopro.ctos_follow('HS_TOOBSERVER', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3671
    var len2, m, player, ref2, room;
3672 3673 3674 3675
    room = ROOM_all[client.rid];
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
3676
    if (room.hostinfo.no_watch) {
nanahira's avatar
nanahira committed
3677 3678 3679
      ygopro.stoc_send_chat(client, "${watch_denied_room}", ygopro.constants.COLORS.RED);
      return true;
    }
nanahira's avatar
nanahira committed
3680
    if ((!room.arena && !settings.modules.challonge.enabled) || client.is_local) {
3681 3682
      return false;
    }
nanahira's avatar
nanahira committed
3683
    ref2 = room.players;
nanahira's avatar
nanahira committed
3684 3685
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      player = ref2[m];
3686 3687 3688 3689 3690 3691 3692
      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
3693

nanahira's avatar
nanahira committed
3694
  ygopro.ctos_follow('HS_KICK', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3695
    var len2, m, player, ref2, room;
3696 3697
    room = ROOM_all[client.rid];
    if (!room) {
3698 3699
      return;
    }
nanahira's avatar
nanahira committed
3700
    ref2 = room.players;
nanahira's avatar
nanahira committed
3701 3702
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      player = ref2[m];
mercury233's avatar
mercury233 committed
3703
      if (player && player.pos === info.pos && player !== client) {
nanahira's avatar
nanahira committed
3704
        if (room.arena === "athletic" || settings.modules.challonge.enabled) {
nanahira's avatar
nanahira committed
3705
          ygopro.stoc_send_chat_to_room(room, `${client.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3706
          CLIENT_kick(client);
3707 3708
          return true;
        }
mercury233's avatar
mercury233 committed
3709
        client.kick_count = client.kick_count ? client.kick_count + 1 : 1;
mercury233's avatar
fix  
mercury233 committed
3710
        if (client.kick_count >= 5 && room.random_type) {
nanahira's avatar
nanahira committed
3711
          ygopro.stoc_send_chat_to_room(room, `${client.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
mercury233's avatar
mercury233 committed
3712
          ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}");
nanahira's avatar
nanahira committed
3713
          CLIENT_kick(client);
mercury233's avatar
mercury233 committed
3714 3715
          return true;
        }
nanahira's avatar
nanahira committed
3716
        ygopro.stoc_send_chat_to_room(room, `${player.name} \${kicked_by_player}`, ygopro.constants.COLORS.RED);
3717 3718 3719 3720 3721
      }
    }
    return false;
  });

nanahira's avatar
nanahira committed
3722
  ygopro.stoc_follow('TYPE_CHANGE', true, function(buffer, info, client, server, datas) {
神楽坂玲奈's avatar
神楽坂玲奈 committed
3723 3724 3725
    var is_host, selftype;
    selftype = info.type & 0xf;
    is_host = ((info.type >> 4) & 0xf) !== 0;
nanahira's avatar
nanahira committed
3726 3727 3728
    // if room and room.hostinfo.no_watch and selftype == 7
    //   ygopro.stoc_die(client, "${watch_denied_room}")
    //   return true
神楽坂玲奈's avatar
神楽坂玲奈 committed
3729
    client.is_host = is_host;
mercury233's avatar
test3  
mercury233 committed
3730
    client.pos = selftype;
nanahira's avatar
nanahira committed
3731
    //console.log "TYPE_CHANGE to #{client.name}:", info, selftype, is_host
nanahira's avatar
nanahira committed
3732
    return false;
神楽坂玲奈's avatar
神楽坂玲奈 committed
3733 3734
  });

3735 3736 3737
  ygopro.stoc_follow('HS_PLAYER_ENTER', true, function(buffer, info, client, server, datas) {
    var pos, room, struct;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
3738
    if (!(room && settings.modules.hide_name && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN)) {
3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750
      return false;
    }
    pos = info.pos;
    if (pos < 4 && pos !== client.pos) {
      struct = ygopro.structs["STOC_HS_PlayerEnter"];
      struct._setBuff(buffer);
      struct.set("name", "********");
      buffer = struct.buffer;
    }
    return false;
  });

nanahira's avatar
nanahira committed
3751
  ygopro.stoc_follow('HS_PLAYER_CHANGE', false, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3752
    var is_ready, len2, len3, m, n, p1, p2, player, pos, ref2, ref3, room;
3753 3754
    room = ROOM_all[client.rid];
    if (!(room && room.max_player && client.is_host)) {
3755 3756 3757 3758
      return;
    }
    pos = info.status >> 4;
    is_ready = (info.status & 0xf) === 9;
3759
    if (pos < room.max_player) {
3760 3761
      if (room.arena) {
        room.ready_player_count = 0;
nanahira's avatar
nanahira committed
3762
        ref2 = room.players;
nanahira's avatar
nanahira committed
3763 3764
        for (m = 0, len2 = ref2.length; m < len2; m++) {
          player = ref2[m];
3765 3766 3767
          if (player.pos === pos) {
            player.is_ready = is_ready;
          }
3768
        }
3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790
        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
3791
          room.waiting_for_player_time = settings.modules.arena_mode.ready_time;
3792 3793 3794 3795 3796 3797
          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
3798
          room.waiting_for_player_time = settings.modules.arena_mode.ready_time;
3799 3800 3801
        }
      } else {
        room.ready_player_count_without_host = 0;
nanahira's avatar
nanahira committed
3802
        ref3 = room.players;
nanahira's avatar
nanahira committed
3803 3804
        for (n = 0, len3 = ref3.length; n < len3; n++) {
          player = ref3[n];
3805 3806 3807 3808 3809 3810 3811 3812
          if (player.pos === pos) {
            player.is_ready = is_ready;
          }
          if (!player.is_host) {
            room.ready_player_count_without_host += player.is_ready;
          }
        }
        if (room.ready_player_count_without_host >= room.max_player - 1) {
nanahira's avatar
nanahira committed
3813
          //log.info "all ready"
3814
          setTimeout((function() {
nanahira's avatar
nanahira committed
3815
            wait_room_start(ROOM_all[client.rid], settings.modules.random_duel.ready_time);
3816
          }), 1000);
3817 3818 3819 3820 3821
        }
      }
    }
  });

nanahira's avatar
nanahira committed
3822
  ygopro.ctos_follow('REQUEST_FIELD', true, function(buffer, info, client, server, datas) {
3823 3824 3825
    return true;
  });

nanahira's avatar
nanahira committed
3826
  ygopro.stoc_follow('FIELD_FINISH', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
3827 3828
    var room;
    room = ROOM_all[client.rid];
3829
    if (!(room && settings.modules.reconnect.enabled)) {
nanahira's avatar
nanahira committed
3830 3831
      return true;
    }
3832
    client.reconnecting = false;
nanahira's avatar
nanahira committed
3833
    if (client.time_confirm_required) { // client did not send TIME_CONFIRM
3834
      client.waiting_for_last = true;
nanahira's avatar
nanahira committed
3835
    } else if (client.last_game_msg && client.last_game_msg_title !== 'WAITING') { // client sent TIME_CONFIRM
3836 3837 3838 3839
      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
3840 3841 3842 3843
    }
    return true;
  });

3844
  ygopro.stoc_follow('DUEL_END', false, function(buffer, info, client, server, datas) {
nanahira's avatar
js  
nanahira committed
3845
    var len2, len3, m, n, player, ref2, ref3, results, room;
nanahira's avatar
move  
nanahira committed
3846 3847 3848 3849
    room = ROOM_all[client.rid];
    if (!(room && settings.modules.replay_delay && room.hostinfo.mode === 1)) {
      return;
    }
3850
    SOCKET_flush_data(client, datas);
nanahira's avatar
move  
nanahira committed
3851
    CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
3852
    if (!room.replays_sent_to_watchers) {
nanahira's avatar
move  
nanahira committed
3853
      room.replays_sent_to_watchers = true;
nanahira's avatar
js  
nanahira committed
3854
      ref2 = room.players;
nanahira's avatar
move  
nanahira committed
3855 3856
      for (m = 0, len2 = ref2.length; m < len2; m++) {
        player = ref2[m];
nanahira's avatar
js  
nanahira committed
3857 3858 3859 3860 3861 3862 3863 3864
        if (player && player.pos > 3) {
          CLIENT_send_replays(player, room);
        }
      }
      ref3 = room.watchers;
      results = [];
      for (n = 0, len3 = ref3.length; n < len3; n++) {
        player = ref3[n];
nanahira's avatar
move  
nanahira committed
3865 3866 3867 3868 3869 3870 3871 3872
        if (player) {
          results.push(CLIENT_send_replays(player, room));
        }
      }
      return results;
    }
  });

3873
  wait_room_start = function(room, time) {
nanahira's avatar
nanahira committed
3874
    var len2, m, player, ref2;
nanahira's avatar
nanahira committed
3875
    if (room && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.ready_player_count_without_host >= room.max_player - 1) {
3876 3877 3878
      time -= 1;
      if (time) {
        if (!(time % 5)) {
nanahira's avatar
nanahira committed
3879
          ygopro.stoc_send_chat_to_room(room, `${time <= 9 ? ' ' : ''}${time}\${kick_count_down}`, time <= 9 ? ygopro.constants.COLORS.RED : ygopro.constants.COLORS.LIGHTBLUE);
3880 3881 3882 3883 3884
        }
        setTimeout((function() {
          wait_room_start(room, time);
        }), 1000);
      } else {
nanahira's avatar
nanahira committed
3885
        ref2 = room.players;
nanahira's avatar
nanahira committed
3886 3887
        for (m = 0, len2 = ref2.length; m < len2; m++) {
          player = ref2[m];
mercury233's avatar
fix  
mercury233 committed
3888
          if (player && player.is_host) {
mercury233's avatar
mercury233 committed
3889
            ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}");
nanahira's avatar
nanahira committed
3890
            ygopro.stoc_send_chat_to_room(room, `${player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3891
            CLIENT_kick(player);
3892 3893 3894 3895 3896 3897
          }
        }
      }
    }
  };

3898
  wait_room_start_arena = function(room) {
nanahira's avatar
fix  
nanahira committed
3899
    var display_name, len2, m, player, ref2;
nanahira's avatar
nanahira committed
3900
    if (room && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.waiting_for_player) {
3901 3902 3903
      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
fix  
nanahira committed
3904 3905 3906 3907 3908 3909 3910
          ref2 = room.players;
          for (m = 0, len2 = ref2.length; m < len2; m++) {
            player = ref2[m];
            if (!(player)) {
              continue;
            }
            display_name = (settings.modules.hide_name && player !== room.waiting_for_player ? "********" : room.waiting_for_player.name);
nanahira's avatar
nanahira committed
3911
            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
3912
          }
3913 3914
        }
      } else {
nanahira's avatar
nanahira committed
3915
        ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
3916
        CLIENT_kick(room.waiting_for_player);
3917 3918 3919 3920 3921 3922 3923 3924
        if (room.waiting_for_player_interval) {
          clearInterval(room.waiting_for_player_interval);
          room.waiting_for_player_interval = null;
        }
      }
    }
  };

nanahira's avatar
nanahira committed
3925
  //tip
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3926
  ygopro.stoc_send_random_tip = function(client) {
mercury233's avatar
mercury233 committed
3927 3928
    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
3929
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3930 3931
  };

3932
  ygopro.stoc_send_random_tip_to_room = function(room) {
mercury233's avatar
mercury233 committed
3933 3934
    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)]);
3935 3936 3937
    }
  };

nanahira's avatar
nanahira committed
3938
  load_tips = global.load_tips = function(callback) {
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
3939
    request({
mercury233's avatar
mercury233 committed
3940
      url: settings.modules.tips.get,
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
3941 3942
      json: true
    }, function(error, response, body) {
mercury233's avatar
mercury233 committed
3943 3944 3945 3946 3947
      if (_.isString(body)) {
        log.warn("tips bad json", body);
      } else if (error || !body) {
        log.warn('tips error', error, response);
      } else {
mercury233's avatar
mercury233 committed
3948 3949
        setting_change(tips, "tips", body);
        log.info("tips loaded", tips.tips.length);
mercury233's avatar
mercury233 committed
3950
      }
nanahira's avatar
nanahira committed
3951 3952 3953
      if (callback) {
        callback(error, body);
      }
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
3954
    });
mercury233's avatar
mercury233 committed
3955 3956
  };

mercury233's avatar
mercury233 committed
3957
  if (settings.modules.tips.get) {
mercury233's avatar
mercury233 committed
3958 3959
    load_tips();
    setInterval(function() {
nanahira's avatar
nanahira committed
3960 3961 3962
      var len2, m, room;
      for (m = 0, len2 = ROOM_all.length; m < len2; m++) {
        room = ROOM_all[m];
3963
        if (room && room.established) {
nanahira's avatar
nanahira committed
3964
          if (room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING || room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
3965 3966
            ygopro.stoc_send_random_tip_to_room(room);
          }
mercury233's avatar
mercury233 committed
3967 3968 3969
        }
      }
    }, 30000);
神楽坂玲奈's avatar
fix  
神楽坂玲奈 committed
3970
  }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
3971

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

nanahira's avatar
nanahira committed
4082
  ygopro.ctos_follow('SURRENDER', true, function(buffer, info, client, server, datas) {
4083 4084 4085 4086 4087
    var room;
    room = ROOM_all[client.rid];
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
4088
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN || room.hostinfo.mode === 2) {
4089 4090
      return true;
    }
4091
    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)) {
4092 4093 4094 4095 4096 4097
      ygopro.stoc_send_chat(client, "${surrender_denied}", ygopro.constants.COLORS.BABYBLUE);
      return true;
    }
    return false;
  });

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

nanahira's avatar
nanahira committed
4338
  ygopro.ctos_follow('UPDATE_DECK', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4339
    var buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, len2, len3, line, m, n, oppo_pos, recover_player_data, room, struct, win_pos;
nanahira's avatar
nanahira committed
4340
    if (settings.modules.reconnect.enabled && client.pre_reconnecting) {
nanahira's avatar
nanahira committed
4341 4342 4343 4344
      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
4345
        CLIENT_reconnect(client);
nanahira's avatar
nanahira committed
4346 4347
      } else if (CLIENT_is_able_to_kick_reconnect(client, buffer)) {
        CLIENT_kick_reconnect(client, buffer);
nanahira's avatar
nanahira committed
4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359
      } 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
4360 4361 4362 4363
    room = ROOM_all[client.rid];
    if (!room) {
      return false;
    }
nanahira's avatar
nanahira committed
4364 4365
    //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
4366 4367 4368
      CLIENT_kick(client);
      return true;
    }
mercury233's avatar
mercury233 committed
4369
    buff_main = (function() {
nanahira's avatar
nanahira committed
4370
      var m, ref2, results;
4371
      results = [];
nanahira's avatar
nanahira committed
4372
      for (i = m = 0, ref2 = info.mainc; (0 <= ref2 ? m < ref2 : m > ref2); i = 0 <= ref2 ? ++m : --m) {
4373
        results.push(info.deckbuf[i]);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4374
      }
4375
      return results;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4376
    })();
mercury233's avatar
mercury233 committed
4377
    buff_side = (function() {
nanahira's avatar
nanahira committed
4378
      var m, ref2, ref3, results;
4379
      results = [];
nanahira's avatar
nanahira committed
4380
      for (i = m = ref2 = info.mainc, ref3 = info.mainc + info.sidec; (ref2 <= ref3 ? m < ref3 : m > ref3); i = ref2 <= ref3 ? ++m : --m) {
4381
        results.push(info.deckbuf[i]);
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4382
      }
4383
      return results;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4384
    })();
mercury233's avatar
mercury233 committed
4385 4386
    client.main = buff_main;
    client.side = buff_side;
nanahira's avatar
nanahira committed
4387
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
nanahira's avatar
fix  
nanahira committed
4388
      client.start_deckbuf = Buffer.from(buffer);
nanahira's avatar
nanahira committed
4389
    }
nanahira's avatar
nanahira committed
4390
    oppo_pos = room.hostinfo.mode === 2 ? 2 : 1;
nanahira's avatar
nanahira committed
4391
    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]) {
4392
      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
4393
      room.finished_by_death = true;
nanahira's avatar
nanahira committed
4394
      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
4395 4396 4397
      if (room.hostinfo.mode === 1) {
        CLIENT_send_replays(room.dueling_players[oppo_pos - win_pos], room);
      }
nanahira's avatar
nanahira committed
4398 4399 4400 4401
      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');
      }
4402
      room.scores[room.dueling_players[oppo_pos - win_pos].name_vpass] = -1;
nanahira's avatar
nanahira committed
4403
      CLIENT_kick(room.dueling_players[oppo_pos - win_pos]);
nanahira's avatar
nanahira committed
4404
      if (room.hostinfo.mode === 2) {
nanahira's avatar
nanahira committed
4405
        CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]);
nanahira's avatar
nanahira committed
4406
      }
nanahira's avatar
nanahira committed
4407 4408
      return true;
    }
nanahira's avatar
nanahira committed
4409 4410
    struct = ygopro.structs["deck"];
    struct._setBuff(buffer);
4411
    if (room.random_type || room.arena) {
nanahira's avatar
nanahira committed
4412
      if (client.pos === 0) {
mercury233's avatar
mercury233 committed
4413 4414 4415
        room.waiting_for_player = room.waiting_for_player2;
      }
      room.last_active_time = moment();
nanahira's avatar
nanahira committed
4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430
    }
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.recovering) {
      recover_player_data = _.find(room.recover_duel_log.players, function(player) {
        return player.real_name === client.name_vpass;
      });
      if (recover_player_data && _.isEqual(buffer, Buffer.from(recover_player_data.deckbuf, "base64"))) {
        if (recover_player_data.is_first) {
          room.determine_firstgo = client;
        }
      } else {
        struct.set("mainc", 1);
        struct.set("sidec", 1);
        struct.set("deckbuf", [4392470, 4392470]);
        ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED);
      }
nanahira's avatar
nanahira committed
4431
    } 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
4432 4433 4434 4435 4436 4437
      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);
4438 4439
      for (m = 0, len2 = decks.length; m < len2; m++) {
        deck = decks[m];
Yuzurisa's avatar
Yuzurisa committed
4440
        if (deck_name_match(deck, client.name)) {
mercury233's avatar
fix  
mercury233 committed
4441 4442
          found_deck = deck;
        }
mercury233's avatar
mercury233 committed
4443 4444
      }
      if (found_deck) {
mercury233's avatar
mercury233 committed
4445
        deck_text = fs.readFileSync(settings.modules.tournament_mode.deck_path + found_deck, {
mercury233's avatar
mercury233 committed
4446 4447 4448 4449 4450 4451
          encoding: "ASCII"
        });
        deck_array = deck_text.split("\n");
        deck_main = [];
        deck_side = [];
        current_deck = deck_main;
4452 4453
        for (n = 0, len3 = deck_array.length; n < len3; n++) {
          line = deck_array[n];
mercury233's avatar
mercury233 committed
4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467
          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
4468 4469
          //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
4470
        } else {
nanahira's avatar
nanahira committed
4471 4472
          //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
4473 4474
        }
      } else {
nanahira's avatar
nanahira committed
4475 4476
        //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
4477
      }
mercury233's avatar
mercury233 committed
4478
    }
mercury233's avatar
mercury233 committed
4479
    return false;
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4480 4481
  });

nanahira's avatar
nanahira committed
4482
  ygopro.ctos_follow('RESPONSE', false, function(buffer, info, client, server, datas) {
4483 4484
    var room;
    room = ROOM_all[client.rid];
4485
    if (!(room && (room.random_type || room.arena))) {
4486 4487
      return;
    }
4488
    room.last_active_time = moment();
4489 4490
  });

nanahira's avatar
nanahira committed
4491
  ygopro.stoc_follow('TIME_LIMIT', true, function(buffer, info, client, server, datas) {
4492
    var check, cur_players, room;
nanahira's avatar
nanahira committed
4493
    room = ROOM_all[client.rid];
nanahira's avatar
js  
nanahira committed
4494 4495 4496
    if (!room) {
      return;
    }
nanahira's avatar
nanahira committed
4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524
    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
4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541
    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
4542
    if (check) {
4543
      CLIENT_heartbeat_register(client, false);
nanahira's avatar
nanahira committed
4544
    }
nanahira's avatar
js  
nanahira committed
4545
    return false;
nanahira's avatar
nanahira committed
4546 4547
  });

nanahira's avatar
nanahira committed
4548
  ygopro.ctos_follow('TIME_CONFIRM', false, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4549 4550
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4551
    if (!room) {
nanahira's avatar
nanahira committed
4552 4553
      return;
    }
nanahira's avatar
nanahira committed
4554 4555 4556
    if (room.recovered) {
      room.recovered = false;
    }
nanahira's avatar
nanahira committed
4557
    if (settings.modules.reconnect.enabled) {
4558 4559 4560 4561 4562 4563 4564 4565 4566
      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
4567 4568
      client.time_confirm_required = false;
    }
4569 4570 4571 4572
    if (settings.modules.heartbeat_detection.enabled) {
      client.heartbeat_protected = false;
      client.heartbeat_responsed = true;
      CLIENT_heartbeat_unregister(client);
nanahira's avatar
typo  
nanahira committed
4573
    }
nanahira's avatar
nanahira committed
4574 4575
  });

nanahira's avatar
nanahira committed
4576
  ygopro.ctos_follow('HAND_RESULT', false, function(buffer, info, client, server, datas) {
4577 4578
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4579 4580 4581 4582
    if (!room) {
      return;
    }
    client.selected_preduel = true;
nanahira's avatar
nanahira committed
4583 4584 4585 4586 4587
    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');
4588 4589 4590
    }
  });

nanahira's avatar
nanahira committed
4591
  ygopro.ctos_follow('TP_RESULT', false, function(buffer, info, client, server, datas) {
4592 4593
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4594 4595 4596 4597
    if (!room) {
      return;
    }
    client.selected_preduel = true;
nanahira's avatar
nanahira committed
4598
    // room.selecting_tp = false
nanahira's avatar
nanahira committed
4599
    if (!(room.random_type || room.arena)) {
4600 4601
      return;
    }
4602
    room.last_active_time = moment();
4603 4604
  });

nanahira's avatar
nanahira committed
4605
  ygopro.stoc_follow('CHAT', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4606
    var len2, m, pid, player, ref2, room, tcolor, tplayer;
nanahira's avatar
nanahira committed
4607 4608
    room = ROOM_all[client.rid];
    pid = info.player;
nanahira's avatar
nanahira committed
4609
    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
4610 4611
      return;
    }
nanahira's avatar
nanahira committed
4612
    if (room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING && !room.dueling_players[0].is_first) {
nanahira's avatar
fix  
nanahira committed
4613
      if (room.hostinfo.mode === 2) {
nanahira's avatar
fix  
nanahira committed
4614 4615 4616 4617 4618 4619
        pid = {
          0: 2,
          1: 3,
          2: 0,
          3: 1
        }[pid];
nanahira's avatar
fix  
nanahira committed
4620 4621 4622 4623
      } else {
        pid = 1 - pid;
      }
    }
nanahira's avatar
nanahira committed
4624
    ref2 = room.players;
nanahira's avatar
nanahira committed
4625 4626
    for (m = 0, len2 = ref2.length; m < len2; m++) {
      player = ref2[m];
nanahira's avatar
nanahira committed
4627 4628 4629 4630 4631 4632 4633
      if (player && player.pos === pid) {
        tplayer = player;
      }
    }
    if (!tplayer) {
      return;
    }
nanahira's avatar
fi  
nanahira committed
4634
    tcolor = chat_color.save_list[CLIENT_get_authorize_key(tplayer)];
nanahira's avatar
nanahira committed
4635 4636 4637 4638 4639 4640 4641 4642 4643
    if (tcolor) {
      ygopro.stoc_send(client, 'CHAT', {
        player: ygopro.constants.COLORS[tcolor],
        msg: tplayer.name + ": " + info.msg
      });
      return true;
    }
  });

nanahira's avatar
nanahira committed
4644
  ygopro.stoc_follow('SELECT_HAND', true, function(buffer, info, client, server, datas) {
4645 4646
    var room;
    room = ROOM_all[client.rid];
nanahira's avatar
nanahira committed
4647
    if (!room) {
nanahira's avatar
nanahira committed
4648
      return false;
nanahira's avatar
nanahira committed
4649 4650
    }
    if (client.pos === 0) {
nanahira's avatar
nanahira committed
4651
      room.duel_stage = ygopro.constants.DUEL_STAGE.FINGER;
nanahira's avatar
nanahira committed
4652
    }
nanahira's avatar
nanahira committed
4653 4654 4655 4656 4657 4658 4659
    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');
4660
    }
nanahira's avatar
nanahira committed
4661 4662 4663 4664 4665
    if (room.determine_firstgo) {
      ygopro.ctos_send(server, "HAND_RESULT", {
        res: client.pos === 0 ? 2 : 1
      });
      return true;
4666
    } else {
nanahira's avatar
nanahira committed
4667
      client.selected_preduel = false;
4668
    }
nanahira's avatar
nanahira committed
4669
    return false;
4670 4671
  });

nanahira's avatar
nanahira committed
4672
  ygopro.stoc_follow('HAND_RESULT', true, function(buffer, info, client, server, datas) {
4673 4674
    var room;
    room = ROOM_all[client.rid];
4675
    if (!room) {
nanahira's avatar
nanahira committed
4676 4677 4678 4679 4680 4681 4682 4683 4684 4685
      return false;
    }
    return room.determine_firstgo;
  });

  ygopro.stoc_follow('SELECT_TP', true, function(buffer, info, client, server, datas) {
    var room;
    room = ROOM_all[client.rid];
    if (!room) {
      return false;
4686
    }
nanahira's avatar
nanahira committed
4687
    room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO;
mercury233's avatar
fix  
mercury233 committed
4688
    if (room.random_type || room.arena) {
4689 4690 4691
      room.waiting_for_player = client;
      room.last_active_time = moment();
    }
nanahira's avatar
nanahira committed
4692 4693 4694 4695 4696 4697 4698 4699 4700 4701
    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;
4702 4703
  });

nanahira's avatar
nanahira committed
4704
  ygopro.stoc_follow('CHANGE_SIDE', false, function(buffer, info, client, server, datas) {
nanahira's avatar
js  
nanahira committed
4705
    var room, room_name, sinterval, temp_log;
4706
    room = ROOM_all[client.rid];
4707
    if (!room) {
mercury233's avatar
mercury233 committed
4708 4709
      return;
    }
nanahira's avatar
nanahira committed
4710 4711 4712
    if (client.pos === 0) {
      room.duel_stage = ygopro.constants.DUEL_STAGE.SIDING;
    }
nanahira's avatar
nanahira committed
4713
    client.selected_preduel = false;
nanahira's avatar
nanahira committed
4714 4715
    if (settings.modules.side_timeout) {
      client.side_tcount = settings.modules.side_timeout;
nanahira's avatar
nanahira committed
4716
      ygopro.stoc_send_chat(client, `\${side_timeout_part1}${settings.modules.side_timeout}\${side_timeout_part2}`, ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4717
      sinterval = setInterval(function() {
nanahira's avatar
nanahira committed
4718
        if (!(room && client && client.side_tcount && room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING)) {
nanahira's avatar
nanahira committed
4719 4720 4721 4722
          clearInterval(sinterval);
          return;
        }
        if (client.side_tcount === 1) {
nanahira's avatar
fix  
nanahira committed
4723
          ygopro.stoc_send_chat_to_room(room, client.name + "${side_overtime_room}", ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4724
          ygopro.stoc_send_chat(client, "${side_overtime}", ygopro.constants.COLORS.RED);
nanahira's avatar
nanahira committed
4725
          //room.scores[client.name_vpass] = -9
nanahira's avatar
nanahira committed
4726
          CLIENT_send_replays(client, room);
nanahira's avatar
nanahira committed
4727
          CLIENT_kick(client);
nanahira's avatar
nanahira committed
4728 4729 4730
          return clearInterval(sinterval);
        } else {
          client.side_tcount = client.side_tcount - 1;
nanahira's avatar
nanahira committed
4731
          return ygopro.stoc_send_chat(client, `\${side_remain_part1}${client.side_tcount}\${side_remain_part2}`, ygopro.constants.COLORS.BABYBLUE);
nanahira's avatar
nanahira committed
4732 4733 4734 4735
        }
      }, 60000);
      client.side_interval = sinterval;
    }
nanahira's avatar
js  
nanahira committed
4736
    if (settings.modules.challonge.enabled && settings.modules.challonge.post_score_midduel && room.hostinfo.mode !== 2 && client.pos === 0) {
nanahira's avatar
nanahira committed
4737
      temp_log = JSON.parse(JSON.stringify(room.get_challonge_score()));
nanahira's avatar
nanahira committed
4738
      delete temp_log.winnerId;
nanahira's avatar
js  
nanahira committed
4739
      room_name = room.name;
nanahira's avatar
nanahira committed
4740
      challonge.matches._update({
4741
        id: settings.modules.challonge.tournament_id,
nanahira's avatar
nanahira committed
4742 4743 4744 4745
        matchId: room.challonge_info.id,
        match: temp_log,
        callback: function(err, data) {
          if (err) {
nanahira's avatar
js  
nanahira committed
4746
            log.warn("Errored pushing scores to Challonge.", room_name, err);
nanahira's avatar
nanahira committed
4747 4748
          } else {
            refresh_challonge_cache();
nanahira's avatar
nanahira committed
4749 4750 4751 4752
          }
        }
      });
    }
4753
    if (room.random_type || room.arena) {
nanahira's avatar
nanahira committed
4754
      if (client.pos === 0) {
4755 4756 4757 4758 4759
        room.waiting_for_player = client;
      } else {
        room.waiting_for_player2 = client;
      }
      room.last_active_time = moment();
mercury233's avatar
mercury233 committed
4760 4761 4762
    }
  });

nanahira's avatar
nanahira committed
4763
  ygopro.stoc_follow('REPLAY', true, function(buffer, info, client, server, datas) {
nanahira's avatar
nanahira committed
4764
    var duellog, dueltime, i, len2, len3, m, n, player, ref2, ref3, replay_filename, room;
4765 4766
    room = ROOM_all[client.rid];
    if (!room) {
4767
      return settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.block_replay_to_player || settings.modules.replay_delay;
4768
    }
mercury233's avatar
mercury233 committed
4769
    if (settings.modules.cloud_replay.enabled && room.random_type) {
mercury233's avatar
fix  
mercury233 committed
4770 4771
      Cloud_replay_ids.push(room.cloud_replay_id);
    }
nanahira's avatar
nanahira committed
4772
    if (!room.replays[room.duel_count - 1]) {
nanahira's avatar
nanahira committed
4773
      // console.log("Replay saved: ", room.duel_count - 1, client.pos)
4774
      room.replays[room.duel_count - 1] = buffer;
nanahira's avatar
nanahira committed
4775
    }
4776
    if (settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.replay_safe || settings.modules.tournament_mode.enable_recover) {
nanahira's avatar
nanahira committed
4777
      if (client.pos === 0) {
nanahira's avatar
nanahira committed
4778
        dueltime = moment().format('YYYY-MM-DD HH-mm-ss');
mercury233's avatar
mercury233 committed
4779
        replay_filename = dueltime;
nanahira's avatar
fix  
nanahira committed
4780
        if (room.hostinfo.mode !== 2) {
nanahira's avatar
nanahira committed
4781
          ref2 = room.dueling_players;
nanahira's avatar
nanahira committed
4782
          for (i = m = 0, len2 = ref2.length; m < len2; i = ++m) {
nanahira's avatar
nanahira committed
4783
            player = ref2[i];
nanahira's avatar
nanahira committed
4784 4785 4786
            replay_filename = replay_filename + (i > 0 ? " VS " : " ") + player.name;
          }
        } else {
nanahira's avatar
nanahira committed
4787
          ref3 = room.dueling_players;
nanahira's avatar
nanahira committed
4788
          for (i = n = 0, len3 = ref3.length; n < len3; i = ++n) {
nanahira's avatar
nanahira committed
4789
            player = ref3[i];
nanahira's avatar
nanahira committed
4790 4791
            replay_filename = replay_filename + (i > 0 ? (i === 2 ? " VS " : " & ") : " ") + player.name;
          }
mercury233's avatar
mercury233 committed
4792 4793
        }
        replay_filename = replay_filename.replace(/[\/\\\?\*]/g, '_') + ".yrp";
mercury233's avatar
mercury233 committed
4794
        duellog = {
nanahira's avatar
nanahira committed
4795
          id: duel_log.duel_log.length + 1,
mercury233's avatar
mercury233 committed
4796
          time: dueltime,
4797
          name: room.name + (settings.modules.tournament_mode.show_info ? " (Duel:" + room.duel_count + ")" : ""),
nanahira's avatar
nanahira committed
4798
          roomid: room.process_pid.toString(),
mercury233's avatar
mercury233 committed
4799
          cloud_replay_id: "R#" + room.cloud_replay_id,
mercury233's avatar
mercury233 committed
4800
          replay_filename: replay_filename,
nanahira's avatar
nanahira committed
4801
          roommode: room.hostinfo.mode,
mercury233's avatar
mercury233 committed
4802
          players: (function() {
nanahira's avatar
nanahira committed
4803
            var len4, o, ref4, results;
nanahira's avatar
nanahira committed
4804
            ref4 = room.dueling_players;
mercury233's avatar
mercury233 committed
4805
            results = [];
nanahira's avatar
nanahira committed
4806 4807
            for (o = 0, len4 = ref4.length; o < len4; o++) {
              player = ref4[o];
mercury233's avatar
mercury233 committed
4808
              results.push({
nanahira's avatar
nanahira committed
4809 4810
                real_name: player.name_vpass,
                deckbuf: player.start_deckbuf.toString("base64"),
nanahira's avatar
nanahira committed
4811
                pos: player.pos,
nanahira's avatar
nanahira committed
4812
                is_first: player.is_first,
4813
                name: player.name + (settings.modules.tournament_mode.show_ip && !player.is_local ? " (IP: " + player.ip.slice(7) + ")" : "") + (settings.modules.tournament_mode.show_info && !(room.hostinfo.mode === 2 && player.pos % 2 > 0) ? " (Score:" + room.scores[player.name_vpass] + " LP:" + (player.lp != null ? player.lp : room.hostinfo.start_lp) + (room.hostinfo.mode !== 2 ? " Cards:" + (player.card_count != null ? player.card_count : room.hostinfo.start_hand) : "") + ")" : ""),
mercury233's avatar
mercury233 committed
4814 4815 4816 4817 4818 4819
                winner: player.pos === room.winner
              });
            }
            return results;
          })()
        };
mercury233's avatar
mercury233 committed
4820 4821
        duel_log.duel_log.unshift(duellog);
        setting_save(duel_log);
mercury233's avatar
mercury233 committed
4822 4823 4824 4825 4826
        fs.writeFile(settings.modules.tournament_mode.replay_path + replay_filename, buffer, function(err) {
          if (err) {
            return log.warn("SAVE REPLAY ERROR", replay_filename, err);
          }
        });
mercury233's avatar
mercury233 committed
4827
      }
mercury233's avatar
mercury233 committed
4828
      if (settings.modules.cloud_replay.enabled) {
nanahira's avatar
nanahira committed
4829
        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
4830
      }
nanahira's avatar
nanahira committed
4831
      return settings.modules.tournament_mode.block_replay_to_player || settings.modules.replay_delay && room.hostinfo.mode === 1;
4832
    } else {
nanahira's avatar
nanahira committed
4833
      return settings.modules.replay_delay && room.hostinfo.mode === 1;
4834 4835 4836
    }
  });

mercury233's avatar
mercury233 committed
4837
  if (settings.modules.random_duel.enabled) {
mercury233's avatar
mercury233 committed
4838
    setInterval(function() {
nanahira's avatar
nanahira committed
4839
      _async.each(ROOM_all, function(room, done) {
nanahira's avatar
nanahira committed
4840
        var time_passed;
nanahira's avatar
nanahira committed
4841
        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)) {
nanahira's avatar
nanahira committed
4842 4843 4844
          done();
          return;
        }
mercury233's avatar
mercury233 committed
4845
        time_passed = Math.floor((moment() - room.last_active_time) / 1000);
nanahira's avatar
nanahira committed
4846
        //log.info time_passed
4847
        if (time_passed >= settings.modules.random_duel.hang_timeout) {
mercury233's avatar
mercury233 committed
4848
          room.last_active_time = moment();
mercury233's avatar
mercury233 committed
4849
          ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}");
4850
          room.scores[room.waiting_for_player.name_vpass] = -9;
nanahira's avatar
nanahira committed
4851 4852
          //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);
nanahira's avatar
nanahira committed
4853
          CLIENT_send_replays(room.waiting_for_player, room);
nanahira's avatar
nanahira committed
4854
          CLIENT_kick(room.waiting_for_player);
4855
        } else if (time_passed >= (settings.modules.random_duel.hang_timeout - 20) && !(time_passed % 10)) {
nanahira's avatar
nanahira committed
4856
          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
4857
          ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}");
mercury233's avatar
mercury233 committed
4858
        }
nanahira's avatar
nanahira committed
4859
        done();
nanahira's avatar
nanahira committed
4860
      });
mercury233's avatar
mercury233 committed
4861 4862 4863
    }, 1000);
  }

4864 4865
  if (settings.modules.mycard.enabled) {
    setInterval(function() {
nanahira's avatar
nanahira committed
4866
      _async.each(ROOM_all, function(room, done) {
nanahira's avatar
nanahira committed
4867
        var time_passed;
nanahira's avatar
nanahira committed
4868
        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)) {
nanahira's avatar
nanahira committed
4869 4870 4871
          done();
          return;
        }
4872
        time_passed = Math.floor((moment() - room.last_active_time) / 1000);
nanahira's avatar
nanahira committed
4873
        //log.info time_passed
4874
        if (time_passed >= settings.modules.random_duel.hang_timeout) {
4875
          room.last_active_time = moment();
nanahira's avatar
nanahira committed
4876
          ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
4877
          room.scores[room.waiting_for_player.name_vpass] = -9;
nanahira's avatar
nanahira committed
4878
          //log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
nanahira's avatar
nanahira committed
4879
          CLIENT_send_replays(room.waiting_for_player, room);
nanahira's avatar
js  
nanahira committed
4880
          CLIENT_kick(room.waiting_for_player);
4881
        } else if (time_passed >= (settings.modules.random_duel.hang_timeout - 20) && !(time_passed % 10)) {
nanahira's avatar
nanahira committed
4882
          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);
4883
        }
nanahira's avatar
nanahira committed
4884 4885
        done();
      });
4886
      if (settings.modules.arena_mode.punish_quit_before_match) {
nanahira's avatar
nanahira committed
4887
        _async.each(ROOM_all, function(room, done) {
nanahira's avatar
nanahira committed
4888
          var player, waited_time;
nanahira's avatar
nanahira committed
4889 4890 4891 4892
          if (!(room && room.arena && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.get_playing_player().length < 2)) {
            done();
            return;
          }
4893 4894 4895 4896 4897 4898 4899 4900 4901
          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);
            }
nanahira's avatar
nanahira committed
4902
          }
nanahira's avatar
nanahira committed
4903 4904
          done();
        });
nanahira's avatar
nanahira committed
4905
      }
4906 4907 4908
    }, 1000);
  }

nanahira's avatar
nanahira committed
4909 4910
  if (settings.modules.heartbeat_detection.enabled) {
    setInterval(function() {
nanahira's avatar
nanahira committed
4911
      _async.each(ROOM_all, function(room, done) {
nanahira's avatar
nanahira committed
4912
        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) {
nanahira's avatar
nanahira committed
4913
          return _async.each(room.get_playing_player(), function(player, _done) {
nanahira's avatar
nanahira committed
4914
            if (player && (room.duel_stage !== ygopro.constants.DUEL_STAGE.SIDING || player.selected_preduel)) {
nanahira's avatar
nanahira committed
4915 4916
              CLIENT_heartbeat_register(player, true);
            }
nanahira's avatar
nanahira committed
4917 4918 4919 4920
            return _done();
          }, done);
        } else {
          return done();
nanahira's avatar
nanahira committed
4921
        }
nanahira's avatar
nanahira committed
4922
      });
nanahira's avatar
nanahira committed
4923 4924 4925
    }, settings.modules.heartbeat_detection.interval);
  }

nanahira's avatar
nanahira committed
4926
  setInterval(function() {
nanahira's avatar
nanahira committed
4927
    var current_time;
nanahira's avatar
nanahira committed
4928
    current_time = moment();
nanahira's avatar
nanahira committed
4929 4930 4931 4932
    return _async.each(ROOM_all, function(room, done) {
      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) {
        room.auto_death_triggered = true;
        room.start_death();
nanahira's avatar
nanahira committed
4933
      }
nanahira's avatar
nanahira committed
4934 4935
      return done();
    });
nanahira's avatar
nanahira committed
4936 4937
  }, 1000);

nanahira's avatar
nanahira committed
4938
  // spawn windbot
nanahira's avatar
nanahira committed
4939 4940
  windbot_looplimit = 0;

nanahira's avatar
nanahira committed
4941
  windbot_process = global.windbot_process = null;
nanahira's avatar
nanahira committed
4942

nanahira's avatar
nanahira committed
4943
  spawn_windbot = global.spawn_windbot = function() {
nanahira's avatar
nanahira committed
4944
    var windbot_bin, windbot_parameters;
mercury233's avatar
mercury233 committed
4945 4946 4947 4948 4949 4950 4951 4952 4953 4954
    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
4955 4956 4957 4958
      cwd: 'windbot'
    });
    windbot_process.on('error', function(err) {
      log.warn('WindBot ERROR', err);
nanahira's avatar
nanahira committed
4959
      if (windbot_looplimit < 1000 && !global.rebooted) {
nanahira's avatar
nanahira committed
4960 4961 4962
        windbot_looplimit++;
        spawn_windbot();
      }
mercury233's avatar
mercury233 committed
4963 4964 4965
    });
    windbot_process.on('exit', function(code) {
      log.warn('WindBot EXIT', code);
nanahira's avatar
nanahira committed
4966
      if (windbot_looplimit < 1000 && !global.rebooted) {
nanahira's avatar
nanahira committed
4967 4968 4969
        windbot_looplimit++;
        spawn_windbot();
      }
mercury233's avatar
mercury233 committed
4970 4971 4972 4973 4974
    });
    windbot_process.stdout.setEncoding('utf8');
    windbot_process.stdout.on('data', function(data) {
      log.info('WindBot:', data);
    });
mercury233's avatar
fix  
mercury233 committed
4975
    windbot_process.stderr.setEncoding('utf8');
nanahira's avatar
nanahira committed
4976
    windbot_process.stderr.on('data', function(data) {
mercury233's avatar
mercury233 committed
4977 4978
      log.warn('WindBot Error:', data);
    });
nanahira's avatar
nanahira committed
4979 4980 4981 4982
  };

  if (settings.modules.windbot.enabled && settings.modules.windbot.spawn) {
    spawn_windbot();
mercury233's avatar
mercury233 committed
4983
  }
4984

nanahira's avatar
nanahira committed
4985
  global.rebooted = false;
nanahira's avatar
nanahira committed
4986

nanahira's avatar
nanahira committed
4987
  //http
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
4988
  if (settings.modules.http) {
mercury233's avatar
mercury233 committed
4989 4990 4991 4992 4993 4994
    addCallback = function(callback, text) {
      if (!callback) {
        return text;
      }
      return callback + "( " + text + " );";
    };
nanahira's avatar
nanahira committed
4995
    requestListener = async function(request, response) {
nanahira's avatar
nanahira committed
4996
      var archive_args, archive_name, archive_process, check, death_room_found, duellog, err, error, filename, getpath, len2, m, parseQueryString, pass_validated, ref2, replay, roomsjson, u;
4997 4998
      parseQueryString = true;
      u = url.parse(request.url, parseQueryString);
nanahira's avatar
nanahira committed
4999 5000 5001
      //pass_validated = u.query.pass == settings.modules.http.password

      //console.log(u.query.username, u.query.pass)
5002
      if (u.pathname === '/api/getrooms') {
nanahira's avatar
nanahira committed
5003
        pass_validated = (await auth.auth(u.query.username, u.query.pass, "get_rooms", "get_rooms", true));
nanahira's avatar
fix  
nanahira committed
5004
        if (!settings.modules.http.public_roomlist && !pass_validated) {
5005
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5006
          response.end(addCallback(u.query.callback, '{"rooms":[{"roomid":"0","roomname":"密码错误","needpass":"true"}]}'));
5007
        } else {
nanahira's avatar
nanahira committed
5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038
          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() {
                var len2, m, ref2, results;
                ref2 = room.players;
                results = [];
                for (m = 0, len2 = ref2.length; m < len2; m++) {
                  player = ref2[m];
                  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
                    });
                  }
5039
                }
nanahira's avatar
nanahira committed
5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050
                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
            })));
          });
5051
        }
mercury233's avatar
mercury233 committed
5052
      } else if (u.pathname === '/api/duellog' && settings.modules.tournament_mode.enabled) {
nanahira's avatar
nanahira committed
5053
        if (!(await auth.auth(u.query.username, u.query.pass, "duel_log", "duel_log"))) {
mercury233's avatar
mercury233 committed
5054
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5055
          response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"));
mercury233's avatar
mercury233 committed
5056 5057 5058
          return;
        } else {
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5059
          duellog = JSON.stringify(duel_log.duel_log, null, 2);
mercury233's avatar
mercury233 committed
5060
          response.end(addCallback(u.query.callback, duellog));
mercury233's avatar
mercury233 committed
5061
        }
nanahira's avatar
nanahira committed
5062
      } else if (u.pathname === '/api/archive.zip' && settings.modules.tournament_mode.enabled) {
nanahira's avatar
nanahira committed
5063
        if (!(await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay_archive"))) {
nanahira's avatar
nanahira committed
5064 5065 5066 5067 5068
          response.writeHead(403);
          response.end("Invalid password.");
          return;
        } else {
          try {
nanahira's avatar
nanahira committed
5069
            archive_name = moment().format('YYYY-MM-DD HH-mm-ss') + ".zip";
nanahira's avatar
nanahira committed
5070
            archive_args = ["a", "-mx0", "-y", archive_name];
nanahira's avatar
fix  
nanahira committed
5071
            check = false;
nanahira's avatar
nanahira committed
5072
            ref2 = duel_log.duel_log;
nanahira's avatar
nanahira committed
5073 5074
            for (m = 0, len2 = ref2.length; m < len2; m++) {
              replay = ref2[m];
nanahira's avatar
fix  
nanahira committed
5075
              check = true;
nanahira's avatar
nanahira committed
5076 5077
              archive_args.push(replay.replay_filename);
            }
nanahira's avatar
fix  
nanahira committed
5078 5079 5080 5081 5082
            if (!check) {
              response.writeHead(403);
              response.end("Duel logs not found.");
              return;
            }
nanahira's avatar
nanahira committed
5083 5084 5085
            archive_process = spawn(settings.modules.tournament_mode.replay_archive_tool, archive_args, {
              cwd: settings.modules.tournament_mode.replay_path
            });
nanahira's avatar
nanahira committed
5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103
            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
5104
            archive_process.stdout.setEncoding('utf8');
nanahira's avatar
nanahira committed
5105 5106 5107
            archive_process.stdout.on('data', (data) => {
              return log.info("archive process: " + data);
            });
nanahira's avatar
nanahira committed
5108
            archive_process.stderr.setEncoding('utf8');
nanahira's avatar
nanahira committed
5109 5110 5111
            archive_process.stderr.on('data', (data) => {
              return log.warn("archive error: " + data);
            });
nanahira's avatar
nanahira committed
5112 5113 5114 5115 5116 5117
          } catch (error1) {
            error = error1;
            response.writeHead(403);
            response.end("Failed reading replays. " + error);
          }
        }
nanahira's avatar
nanahira committed
5118
      } else if (u.pathname === '/api/clearlog' && settings.modules.tournament_mode.enabled) {
nanahira's avatar
nanahira committed
5119
        if (!(await auth.auth(u.query.username, u.query.pass, "clear_duel_log", "clear_duel_log"))) {
nanahira's avatar
nanahira committed
5120 5121 5122 5123 5124
          response.writeHead(200);
          response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"));
          return;
        } else {
          response.writeHead(200);
5125 5126 5127 5128 5129 5130 5131
          if (settings.modules.tournament_mode.log_save_path) {
            fs.writeFile(settings.modules.tournament_mode.log_save_path + 'duel_log.' + moment().format('YYYY-MM-DD HH-mm-ss') + '.json', JSON.stringify(duel_log, null, 2), function(err) {
              if (err) {
                return log.warn('DUEL LOG SAVE ERROR', err);
              }
            });
          }
5132 5133
          duel_log.duel_log = [];
          setting_save(duel_log);
nanahira's avatar
nanahira committed
5134 5135
          response.end(addCallback(u.query.callback, "[{name:'Success'}]"));
        }
mercury233's avatar
mercury233 committed
5136
      } else if (_.startsWith(u.pathname, '/api/replay') && settings.modules.tournament_mode.enabled) {
nanahira's avatar
nanahira committed
5137
        if (!(await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay"))) {
mercury233's avatar
mercury233 committed
5138 5139 5140 5141
          response.writeHead(403);
          response.end("密码错误");
          return;
        } else {
nanahira's avatar
nanahira committed
5142 5143 5144 5145 5146 5147 5148 5149 5150 5151
          getpath = null;
          filename = null;
          try {
            getpath = u.pathname.split("/");
            filename = path.basename(decodeURIComponent(getpath.pop()));
          } catch (error1) {
            response.writeHead(404);
            response.end("bad filename");
            return;
          }
mercury233's avatar
mercury233 committed
5152 5153 5154
          fs.readFile(settings.modules.tournament_mode.replay_path + filename, function(error, buffer) {
            if (error) {
              response.writeHead(404);
mercury233's avatar
fix  
mercury233 committed
5155
              response.end("未找到文件 " + filename);
mercury233's avatar
mercury233 committed
5156 5157 5158 5159 5160
            } else {
              response.writeHead(200, {
                "Content-Type": "application/octet-stream",
                "Content-Disposition": "attachment"
              });
mercury233's avatar
fix  
mercury233 committed
5161
              response.end(buffer);
mercury233's avatar
mercury233 committed
5162 5163 5164
            }
          });
        }
5165
      } else if (u.pathname === '/api/message') {
nanahira's avatar
nanahira committed
5166 5167 5168 5169
        //if !pass_validated
        //  response.writeHead(200)
        //  response.end(addCallback(u.query.callback, "['密码错误', 0]"))
        //  return
5170
        if (u.query.shout) {
nanahira's avatar
nanahira committed
5171
          if (!(await auth.auth(u.query.username, u.query.pass, "shout", "shout"))) {
nanahira's avatar
nanahira committed
5172 5173 5174 5175
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5176
          _async.each(ROOM_all, function(room) {
5177
            if (room && room.established) {
nanahira's avatar
nanahira committed
5178
              return ygopro.stoc_send_chat_to_room(room, u.query.shout, ygopro.constants.COLORS.YELLOW);
5179
            }
nanahira's avatar
nanahira committed
5180
          });
5181
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5182
          response.end(addCallback(u.query.callback, "['shout ok', '" + u.query.shout + "']"));
5183
        } else if (u.query.stop) {
nanahira's avatar
nanahira committed
5184
          if (!(await auth.auth(u.query.username, u.query.pass, "stop", "stop"))) {
nanahira's avatar
nanahira committed
5185 5186 5187 5188
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
5189 5190 5191
          if (u.query.stop === 'false') {
            u.query.stop = false;
          }
nanahira's avatar
nanahira committed
5192 5193
          response.writeHead(200);
          try {
nanahira's avatar
typo  
nanahira committed
5194
            await util.promisify(setting_change)(settings, 'modules:stop', u.query.stop);
nanahira's avatar
nanahira committed
5195 5196 5197 5198 5199
            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 + "']"));
          }
5200
        } else if (u.query.welcome) {
nanahira's avatar
nanahira committed
5201
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "change_welcome"))) {
nanahira's avatar
nanahira committed
5202 5203 5204 5205
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5206
          try {
nanahira's avatar
typo  
nanahira committed
5207
            await util.promisify(setting_change)(settings, 'modules:stop', u.query.welcome);
nanahira's avatar
nanahira committed
5208 5209 5210 5211 5212
            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
5213
        } else if (u.query.getwelcome) {
nanahira's avatar
nanahira committed
5214
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "get_welcome"))) {
nanahira's avatar
nanahira committed
5215 5216 5217 5218
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
5219
          response.writeHead(200);
mercury233's avatar
mercury233 committed
5220
          response.end(addCallback(u.query.callback, "['get ok', '" + settings.modules.welcome + "']"));
mercury233's avatar
mercury233 committed
5221
        } else if (u.query.loadtips) {
nanahira's avatar
nanahira committed
5222
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "change_tips"))) {
nanahira's avatar
nanahira committed
5223 5224 5225 5226
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5227 5228 5229
          load_tips(function(err) {
            response.writeHead(200);
            if (err) {
nanahira's avatar
nanahira committed
5230
              return response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"));
nanahira's avatar
nanahira committed
5231
            } else {
nanahira's avatar
nanahira committed
5232
              return response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']"));
nanahira's avatar
nanahira committed
5233 5234
            }
          });
mercury233's avatar
mercury233 committed
5235
        } else if (u.query.loaddialogues) {
nanahira's avatar
nanahira committed
5236
          if (!(await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues"))) {
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
          load_dialogues(function(err) {
            response.writeHead(200);
            if (err) {
nanahira's avatar
nanahira committed
5244
              return response.end(addCallback(u.query.callback, "['dialogues fail', '" + settings.modules.dialogues.get + "']"));
nanahira's avatar
nanahira committed
5245
            } else {
nanahira's avatar
nanahira committed
5246
              return response.end(addCallback(u.query.callback, "['dialogues ok', '" + settings.modules.dialogues.get + "']"));
nanahira's avatar
nanahira committed
5247 5248
            }
          });
mercury233's avatar
mercury233 committed
5249
        } else if (u.query.ban) {
nanahira's avatar
nanahira committed
5250
          if (!(await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user"))) {
nanahira's avatar
nanahira committed
5251 5252 5253 5254
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5255 5256 5257 5258 5259 5260 5261 5262
          ban_user(u.query.ban, function(err) {
            response.writeHead(200);
            if (err) {
              return response.end(addCallback(u.query.callback, "['ban fail', '" + u.query.ban + "']"));
            } else {
              return response.end(addCallback(u.query.callback, "['ban ok', '" + u.query.ban + "']"));
            }
          });
nanahira's avatar
nanahira committed
5263
        } else if (u.query.kick) {
nanahira's avatar
nanahira committed
5264
          if (!(await auth.auth(u.query.username, u.query.pass, "kick_user", "kick_user"))) {
nanahira's avatar
nanahira committed
5265 5266 5267 5268
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5269 5270 5271 5272 5273 5274 5275 5276
          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
5277
            }
nanahira's avatar
nanahira committed
5278
          });
nanahira's avatar
nanahira committed
5279
        } else if (u.query.death) {
nanahira's avatar
nanahira committed
5280
          if (!(await auth.auth(u.query.username, u.query.pass, "start_death", "start_death"))) {
nanahira's avatar
nanahira committed
5281 5282 5283 5284
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5285
          death_room_found = false;
nanahira's avatar
nanahira committed
5286 5287 5288 5289 5290
          _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
5291 5292
            if (room.start_death()) {
              death_room_found = true;
nanahira's avatar
nanahira committed
5293
            }
nanahira's avatar
nanahira committed
5294
            done();
nanahira's avatar
nanahira committed
5295 5296 5297 5298 5299 5300 5301 5302
          }, 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
5303
        } else if (u.query.deathcancel) {
nanahira's avatar
nanahira committed
5304
          if (!(await auth.auth(u.query.username, u.query.pass, "start_death", "cancel_death"))) {
nanahira's avatar
nanahira committed
5305 5306 5307 5308
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5309
          death_room_found = false;
nanahira's avatar
nanahira committed
5310
          _async.each(rooms, function(room, done) {
nanahira's avatar
nanahira committed
5311 5312 5313 5314
            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
5315 5316
            if (room.cancel_death()) {
              death_room_found = true;
nanahira's avatar
nanahira committed
5317
            }
nanahira's avatar
nanahira committed
5318 5319 5320 5321 5322 5323 5324 5325 5326
            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
5327
        } else if (u.query.reboot) {
nanahira's avatar
nanahira committed
5328
          if (!(await auth.auth(u.query.username, u.query.pass, "stop", "reboot"))) {
nanahira's avatar
nanahira committed
5329 5330 5331 5332
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['密码错误', 0]"));
            return;
          }
nanahira's avatar
nanahira committed
5333 5334 5335 5336
          ROOM_kick("all", function(err, found) {
            global.rebooted = true;
            if (windbot_process) {
              windbot_process.kill();
nanahira's avatar
nanahira committed
5337
            }
nanahira's avatar
nanahira committed
5338 5339 5340 5341
            response.writeHead(200);
            response.end(addCallback(u.query.callback, "['reboot ok', '" + u.query.reboot + "']"));
            return process.exit();
          });
5342
        } else {
mercury233's avatar
mercury233 committed
5343
          response.writeHead(400);
5344
          response.end();
5345
        }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
5346
      } else {
mercury233's avatar
mercury233 committed
5347
        response.writeHead(400);
mercury233's avatar
test3  
mercury233 committed
5348
        response.end();
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
5349
      }
5350 5351
    };
    http_server = http.createServer(requestListener);
神楽坂玲奈's avatar
神楽坂玲奈 committed
5352
    http_server.listen(settings.modules.http.port);
mercury233's avatar
mercury233 committed
5353
    if (settings.modules.http.ssl.enabled) {
5354 5355 5356 5357 5358 5359
      https = require('https');
      options = {
        cert: fs.readFileSync(settings.modules.http.ssl.cert),
        key: fs.readFileSync(settings.modules.http.ssl.key)
      };
      https_server = https.createServer(options, requestListener);
nanahira's avatar
fix  
nanahira committed
5360 5361 5362
      if (settings.modules.http.websocket_roomlist && roomlist) {
        roomlist.init(https_server, ROOM_all);
      }
5363 5364
      https_server.listen(settings.modules.http.ssl.port);
    }
神楽坂玲奈's avatar
11.7  
神楽坂玲奈 committed
5365
  }
神楽坂玲奈's avatar
神楽坂玲奈 committed
5366

nanahira's avatar
nanahira committed
5367 5368 5369 5370 5371 5372 5373 5374
  if (!fs.existsSync('./plugins')) {
    fs.mkdirSync('./plugins');
  }

  plugin_list = fs.readdirSync("./plugins");

  for (m = 0, len2 = plugin_list.length; m < len2; m++) {
    plugin_filename = plugin_list[m];
nanahira's avatar
nanahira committed
5375
    plugin_path = process.cwd() + "/plugins/" + plugin_filename;
nanahira's avatar
nanahira committed
5376
    require(plugin_path);
nanahira's avatar
nanahira committed
5377
    log.info("Plugin loaded:", plugin_filename);
nanahira's avatar
nanahira committed
5378 5379
  }

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