Commit 1e0613cb authored by nanahira's avatar nanahira

Merge branch 'master' into enter-room-log

parents 78da1f9d 103429ba
Pipeline #10117 passed with stages
in 40 minutes and 22 seconds
......@@ -8,7 +8,7 @@ RUN apt update && \
rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/*
# windbot
RUN git clone --depth=1 https://github.com/nanahira/windbot /tmp/windbot && \
RUN git clone --depth=1 https://code.mycard.moe/nanahira/windbot /tmp/windbot && \
cd /tmp/windbot && \
xbuild /property:Configuration=Release /property:TargetFrameworkVersion="v4.5" && \
mv /tmp/windbot/bin/Release /ygopro-server/windbot && \
......
......@@ -11,6 +11,8 @@ const typedefs_json_1 = __importDefault(require("./data/typedefs.json"));
const proto_structs_json_1 = __importDefault(require("./data/proto_structs.json"));
const constants_json_1 = __importDefault(require("./data/constants.json"));
class Handler {
handler;
synchronous;
constructor(handler, synchronous) {
this.handler = handler;
this.synchronous = synchronous || false;
......@@ -28,6 +30,13 @@ class Handler {
}
}
class YGOProMessagesHelper {
handlers;
structs;
structs_declaration;
typedefs;
proto_structs;
constants;
singleHandleLimit;
constructor(singleHandleLimit) {
this.handlers = {
STOC: [new Map(),
......@@ -62,7 +71,7 @@ class YGOProMessagesHelper {
this.structs = new Map();
for (let name in this.structs_declaration) {
const declaration = this.structs_declaration[name];
let result = struct_1.Struct();
let result = (0, struct_1.Struct)();
for (let field of declaration) {
if (field.encoding) {
switch (field.encoding) {
......@@ -171,12 +180,13 @@ class YGOProMessagesHelper {
}
handlerCollection.get(translatedProto).push(handlerObj);
}
async handleBuffer(messageBuffer, direction, protoFilter, params) {
async handleBuffer(messageBuffer, direction, protoFilter, params, preconnect = false) {
let feedback = null;
let messageLength = 0;
let bufferProto = 0;
let datas = [];
for (let l = 0; l < this.singleHandleLimit; ++l) {
const limit = preconnect ? 2 : this.singleHandleLimit;
for (let l = 0; l < limit; ++l) {
if (messageLength === 0) {
if (messageBuffer.length >= 2) {
messageLength = messageBuffer.readUInt16LE(0);
......@@ -206,7 +216,14 @@ class YGOProMessagesHelper {
else {
if (messageBuffer.length >= 2 + messageLength) {
const proto = this.constants[direction][bufferProto];
let cancel = proto && protoFilter && underscore_1.default.indexOf(protoFilter, proto) === -1;
let cancel = proto && protoFilter && !protoFilter.includes(proto);
if (cancel && preconnect) {
feedback = {
type: "INVALID_PACKET",
message: `${direction} proto not allowed`
};
break;
}
let buffer = messageBuffer.slice(3, 2 + messageLength);
//console.log(l, direction, proto, cancel);
for (let priority = 0; priority < 4; ++priority) {
......@@ -224,6 +241,12 @@ class YGOProMessagesHelper {
for (let handler of handlerCollection.get(bufferProto)) {
cancel = await handler.handle(buffer, info, datas, params);
if (cancel) {
if (cancel === '_cancel') {
return {
datas: [],
feedback
};
}
break;
}
}
......@@ -246,10 +269,10 @@ class YGOProMessagesHelper {
break;
}
}
if (l === this.singleHandleLimit - 1) {
if (l === limit - 1) {
feedback = {
type: "OVERSIZE",
message: `Oversized ${direction}`
message: `Oversized ${direction} ${limit}`
};
}
}
......
......@@ -8,13 +8,13 @@ import net from "net";
class Handler {
private handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean>;
private handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string>;
synchronous: boolean;
constructor(handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean>, synchronous: boolean) {
constructor(handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string>, synchronous: boolean) {
this.handler = handler;
this.synchronous = synchronous || false;
}
async handle(buffer: Buffer, info: any, datas: Buffer[], params: any) {
async handle(buffer: Buffer, info: any, datas: Buffer[], params: any): Promise<boolean | string> {
if (this.synchronous) {
return !!(await this.handler(buffer, info, datas, params));
} else {
......@@ -208,7 +208,7 @@ export class YGOProMessagesHelper {
});
}
addHandler(protostr: string, handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean>, synchronous: boolean, priority: number) {
addHandler(protostr: string, handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string>, synchronous: boolean, priority: number) {
if (priority < 0 || priority > 4) {
throw "Invalid priority: " + priority;
}
......@@ -226,12 +226,13 @@ export class YGOProMessagesHelper {
handlerCollection.get(translatedProto).push(handlerObj);
}
async handleBuffer(messageBuffer: Buffer, direction: string, protoFilter?: string[], params?: any): Promise<HandleResult> {
async handleBuffer(messageBuffer: Buffer, direction: string, protoFilter?: string[], params?: any, preconnect = false): Promise<HandleResult> {
let feedback: Feedback = null;
let messageLength = 0;
let bufferProto = 0;
let datas: Buffer[] = [];
for (let l = 0; l < this.singleHandleLimit; ++l) {
const limit = preconnect ? 2 : this.singleHandleLimit;
for (let l = 0; l < limit; ++l) {
if (messageLength === 0) {
if (messageBuffer.length >= 2) {
messageLength = messageBuffer.readUInt16LE(0);
......@@ -257,7 +258,14 @@ export class YGOProMessagesHelper {
} else {
if (messageBuffer.length >= 2 + messageLength) {
const proto = this.constants[direction][bufferProto];
let cancel = proto && protoFilter && _.indexOf(protoFilter, proto) === -1;
let cancel: string | boolean = proto && protoFilter && !protoFilter.includes(proto);
if (cancel && preconnect) {
feedback = {
type: "INVALID_PACKET",
message: `${direction} proto not allowed`
};
break;
}
let buffer = messageBuffer.slice(3, 2 + messageLength);
//console.log(l, direction, proto, cancel);
for (let priority = 0; priority < 4; ++priority) {
......@@ -275,6 +283,12 @@ export class YGOProMessagesHelper {
for (let handler of handlerCollection.get(bufferProto)) {
cancel = await handler.handle(buffer, info, datas, params);
if (cancel) {
if (cancel === '_cancel') {
return {
datas: [],
feedback
}
}
break;
}
}
......@@ -296,10 +310,10 @@ export class YGOProMessagesHelper {
break;
}
}
if (l === this.singleHandleLimit - 1) {
if (l === limit - 1) {
feedback = {
type: "OVERSIZE",
message: `Oversized ${direction}`
message: `Oversized ${direction} ${limit}`
};
}
}
......
......@@ -8,6 +8,9 @@ const axios_1 = __importDefault(require("axios"));
const querystring_1 = __importDefault(require("querystring"));
const moment_1 = __importDefault(require("moment"));
class AthleticChecker {
config;
athleticDeckCache;
lastAthleticDeckFetchTime;
constructor(config) {
this.config = config;
}
......@@ -16,7 +19,7 @@ class AthleticChecker {
return deckText;
}
async getAthleticDecks() {
if (this.athleticDeckCache && moment_1.default().diff(this.lastAthleticDeckFetchTime, "seconds") < this.config.ttl) {
if (this.athleticDeckCache && (0, moment_1.default)().diff(this.lastAthleticDeckFetchTime, "seconds") < this.config.ttl) {
return this.athleticDeckCache;
}
const { data } = await axios_1.default.get(this.config.rankURL, {
......@@ -27,7 +30,7 @@ class AthleticChecker {
});
const athleticDecks = data.slice(0, this.config.rankCount).map(m => m.name);
this.athleticDeckCache = athleticDecks;
this.lastAthleticDeckFetchTime = moment_1.default();
this.lastAthleticDeckFetchTime = (0, moment_1.default)();
return athleticDecks;
}
async getDeckType(deck) {
......@@ -42,7 +45,7 @@ class AthleticChecker {
try {
const athleticDecks = await this.getAthleticDecks();
const deckType = await this.getDeckType(deck);
const athletic = athleticDecks.includes(deckType);
const athletic = athleticDecks.findIndex(d => d === deckType) + 1;
return { success: true, athletic, message: null };
}
catch (e) {
......
......@@ -21,7 +21,7 @@ interface AthleticDecksReturnData {
interface ReturnMessage {
success: boolean;
athletic?: boolean;
athletic?: number;
message: string;
}
......@@ -63,8 +63,8 @@ export class AthleticChecker {
try {
const athleticDecks = await this.getAthleticDecks();
const deckType = await this.getDeckType(deck);
const athletic = athleticDecks.includes(deckType);
return { success: true, athletic, message: null }
const athletic = athleticDecks.findIndex(d => d === deckType) + 1;
return { success: true, athletic, message: null };
} catch (e) {
return { success: false, message: e.toString() };
}
......
......@@ -40,35 +40,40 @@ const jszip_1 = __importDefault(require("jszip"));
const fs = __importStar(require("fs"));
require("reflect-metadata");
class DataManager {
config;
log;
ready;
db;
constructor(config, log) {
this.config = config;
this.ready = false;
this.log = log;
this.ready = false;
}
async transaction(fun) {
const runner = this.db.createQueryRunner();
await runner.connect();
await runner.startTransaction();
let result = false;
try {
result = await fun(runner.manager);
}
catch (e) {
result = false;
this.log.warn(`Failed running transaction: ${e.toString()}`);
// @ts-ignore
if (this.config.type !== 'sqlite') {
this.db.transaction(async (mdb) => {
const result = await fun(mdb);
if (!result) {
throw new Error('Rollback requested.');
}
if (result) {
await runner.commitTransaction();
});
}
else {
await runner.rollbackTransaction();
await fun(this.db.manager);
}
}
catch (e) {
this.log.warn(`Transaction failed: ${e.toString()}`);
}
await runner.release();
}
async init() {
this.db = await typeorm_1.createConnection({
this.db = await (0, typeorm_1.createConnection)({
type: "mysql",
synchronize: true,
supportBigNumbers: true,
bigNumberStrings: false,
entities: ["./data-manager/entities/*.js"],
...this.config
});
......@@ -76,8 +81,13 @@ class DataManager {
}
async getCloudReplaysFromKey(key) {
try {
const replays = await this.db.createQueryBuilder(CloudReplay_1.CloudReplay, "replay")
.where("exists (select id from cloud_replay_player where cloud_replay_player.cloudReplayId = replay.id and cloud_replay_player.key = :key)", { key })
const replaysQuery = this.db.createQueryBuilder(CloudReplay_1.CloudReplay, "replay");
const sqb = replaysQuery.subQuery()
.select('splayer.id')
.from(CloudReplayPlayer_1.CloudReplayPlayer, 'splayer')
.where('splayer.cloudReplayId = replay.id')
.andWhere('splayer.key = :key');
const replays = await replaysQuery.where(`exists ${sqb.getQuery()}`, { key })
.orderBy("replay.date", "DESC")
.limit(10)
.leftJoinAndSelect("replay.players", "player")
......@@ -125,7 +135,7 @@ class DataManager {
const replay = new CloudReplay_1.CloudReplay();
replay.id = id;
replay.fromBuffer(buffer);
replay.date = moment_1.default().toDate();
replay.date = (0, moment_1.default)().toDate();
const players = playerInfos.map(p => {
const player = CloudReplayPlayer_1.CloudReplayPlayer.fromPlayerInfo(p);
return player;
......@@ -216,12 +226,12 @@ class DataManager {
if (ban) {
ban.count += count;
const banTime = ban.count > 3 ? Math.pow(2, ban.count - 3) * 2 : 0;
const banDate = moment_1.default(ban.time);
if (moment_1.default().isAfter(banDate)) {
ban.time = moment_1.default().add(banTime, 'm').toDate();
const banDate = (0, moment_1.default)(ban.time);
if ((0, moment_1.default)().isAfter(banDate)) {
ban.time = (0, moment_1.default)().add(banTime, 'm').toDate();
}
else {
ban.time = moment_1.default(banDate).add(banTime, 'm').toDate();
ban.time = (0, moment_1.default)(banDate).add(banTime, 'm').toDate();
}
if (!underscore_1.default.contains(ban.reasons, reason)) {
ban.reasons.push(reason);
......@@ -231,7 +241,7 @@ class DataManager {
else {
ban = new RandomDuelBan_1.RandomDuelBan();
ban.ip = ip;
ban.time = moment_1.default().toDate();
ban.time = (0, moment_1.default)().toDate();
ban.count = count;
ban.reasons = [reason];
ban.needTip = 1;
......@@ -275,18 +285,24 @@ class DataManager {
queryBuilder.andWhere("duelLog.duelCount = :duelCount", { duelCount });
}
if (playerName != null && playerName.length || playerScore != null && !isNaN(playerScore)) {
let innerQuery = "select id from duel_log_player where duel_log_player.duelLogId = duelLog.id";
const sqb = queryBuilder.subQuery()
.select('splayer.id')
.from(DuelLogPlayer_1.DuelLogPlayer, 'splayer')
.where('splayer.duelLogId = duelLog.id');
//let innerQuery = "select id from duel_log_player where duel_log_player.duelLogId = duelLog.id";
const innerQueryParams = {};
if (playerName != null && playerName.length) {
//const escapedPlayerName = this.getEscapedString(playerName);
innerQuery += " and duel_log_player.realName = :playerName";
sqb.andWhere('splayer.realName = :playerName');
//innerQuery += " and duel_log_player.realName = :playerName";
innerQueryParams.playerName = playerName;
}
if (playerScore != null && !isNaN(playerScore)) {
innerQuery += " and duel_log_player.score = :playerScore";
//innerQuery += " and duel_log_player.score = :playerScore";
sqb.andWhere('splayer.score = :playerScore');
innerQueryParams.playerScore = playerScore;
}
queryBuilder.andWhere(`exists (${innerQuery})`, innerQueryParams);
queryBuilder.andWhere(`exists ${sqb.getQuery()}`, innerQueryParams);
}
queryBuilder.orderBy("duelLog.id", "DESC")
.leftJoinAndSelect("duelLog.players", "player");
......@@ -313,8 +329,16 @@ class DataManager {
async getDuelLogFromRecoverSearch(realName) {
const repo = this.db.getRepository(DuelLog_1.DuelLog);
try {
const duelLogs = await repo.createQueryBuilder("duelLog")
.where("startDeckBuffer is not null and currentDeckBuffer is not null and roomMode != 2 and exists (select id from duel_log_player where duel_log_player.duelLogId = duelLog.id and duel_log_player.realName = :realName)", { realName })
const duelLogsQuery = repo.createQueryBuilder("duelLog")
.where('startDeckBuffer is not null')
.andWhere('currentDeckBuffer is not null')
.andWhere('roomMode != 2');
const sqb = duelLogsQuery.subQuery()
.select('splayer.id')
.from(DuelLogPlayer_1.DuelLogPlayer, 'splayer')
.andWhere('splayer.duelLogId = duelLog.id')
.andWhere('splayer.realName = :realName');
const duelLogs = await duelLogsQuery.andWhere(`exists ${sqb.getQuery()}`, { realName })
.orderBy("duelLog.id", "DESC")
.limit(10)
.leftJoinAndSelect("duelLog.players", "player")
......@@ -392,7 +416,7 @@ class DataManager {
async saveDuelLog(name, roomId, cloudReplayId, replayFilename, roomMode, duelCount, playerInfos) {
const duelLog = new DuelLog_1.DuelLog();
duelLog.name = name;
duelLog.time = moment_1.default().toDate();
duelLog.time = (0, moment_1.default)().toDate();
duelLog.roomId = roomId;
duelLog.cloudReplayId = cloudReplayId;
duelLog.replayFileName = replayFilename;
......@@ -599,11 +623,11 @@ class DataManager {
}
const keyType = vipKey.type;
const previousDate = user.vipExpireDate;
if (previousDate && moment_1.default().isBefore(previousDate)) {
user.vipExpireDate = moment_1.default(previousDate).add(keyType, "d").toDate();
if (previousDate && (0, moment_1.default)().isBefore(previousDate)) {
user.vipExpireDate = (0, moment_1.default)(previousDate).add(keyType, "d").toDate();
}
else {
user.vipExpireDate = moment_1.default().add(keyType, "d").toDate();
user.vipExpireDate = (0, moment_1.default)().add(keyType, "d").toDate();
}
user = await mdb.save(user);
vipKey.isUsed = 1;
......@@ -647,7 +671,7 @@ class DataManager {
user = new User_1.User();
user.key = userKey;
}
user.vipExpireDate = moment_1.default(oldVipUserInfo.expire_date).toDate();
user.vipExpireDate = (0, moment_1.default)(oldVipUserInfo.expire_date).toDate();
user.victory = oldVipUserInfo.victory || null;
user.words = oldVipUserInfo.words || null;
user = await mdb.save(user);
......
import moment from "moment";
import bunyan from "bunyan";
import {Connection, ConnectionOptions, createConnection, EntityManager} from "typeorm";
import {Connection, createConnection, EntityManager} from "typeorm";
import {CloudReplay} from "./entities/CloudReplay";
import {CloudReplayPlayer} from "./entities/CloudReplayPlayer";
import {Ban} from "./entities/Ban";
......@@ -16,6 +16,7 @@ import {RandomDuelScore} from "./entities/RandomDuelScore";
import JSZip from "jszip";
import * as fs from "fs";
import "reflect-metadata";
import { MysqlConnectionOptions } from "typeorm/driver/mysql/MysqlConnectionOptions";
interface BasePlayerInfo {
name: string;
......@@ -42,37 +43,34 @@ export interface DuelLogQuery {roomName: string, duelCount: number, playerName:
export class DataManager {
config: ConnectionOptions;
ready: boolean;
db: Connection;
log: bunyan;
constructor(config: ConnectionOptions, log: bunyan) {
this.config = config;
private db: Connection;
constructor(private config: MysqlConnectionOptions, private log: bunyan) {
this.ready = false;
this.log = log;
}
private async transaction(fun: (mdb: EntityManager) => Promise<boolean>) {
const runner = this.db.createQueryRunner();
await runner.connect();
await runner.startTransaction();
let result = false;
try {
result = await fun(runner.manager);
} catch(e) {
result = false;
this.log.warn(`Failed running transaction: ${e.toString()}`)
// @ts-ignore
if (this.config.type !== 'sqlite') {
this.db.transaction(async (mdb) => {
const result = await fun(mdb);
if (!result) {
throw new Error('Rollback requested.');
}
if(result) {
await runner.commitTransaction();
});
} else {
await runner.rollbackTransaction();
await fun(this.db.manager);
}
} catch (e) {
this.log.warn(`Transaction failed: ${e.toString()}`);
}
await runner.release();
}
async init() {
this.db = await createConnection({
type: "mysql",
synchronize: true,
supportBigNumbers: true,
bigNumberStrings: false,
entities: ["./data-manager/entities/*.js"],
...this.config
});
......@@ -80,8 +78,13 @@ export class DataManager {
}
async getCloudReplaysFromKey(key: string) {
try {
const replays = await this.db.createQueryBuilder(CloudReplay, "replay")
.where("exists (select id from cloud_replay_player where cloud_replay_player.cloudReplayId = replay.id and cloud_replay_player.key = :key)", { key })
const replaysQuery = this.db.createQueryBuilder(CloudReplay, "replay");
const sqb = replaysQuery.subQuery()
.select('splayer.id')
.from(CloudReplayPlayer, 'splayer')
.where('splayer.cloudReplayId = replay.id')
.andWhere('splayer.key = :key');
const replays = await replaysQuery.where(`exists ${sqb.getQuery()}`, { key })
.orderBy("replay.date", "DESC")
.limit(10)
.leftJoinAndSelect("replay.players", "player")
......@@ -282,19 +285,25 @@ export class DataManager {
if(duelCount != null && !isNaN(duelCount)) {
queryBuilder.andWhere("duelLog.duelCount = :duelCount", { duelCount });
}
if(playerName != null && playerName.length || playerScore != null && !isNaN(playerScore)) {
let innerQuery = "select id from duel_log_player where duel_log_player.duelLogId = duelLog.id";
if (playerName != null && playerName.length || playerScore != null && !isNaN(playerScore)) {
const sqb = queryBuilder.subQuery()
.select('splayer.id')
.from(DuelLogPlayer, 'splayer')
.where('splayer.duelLogId = duelLog.id');
//let innerQuery = "select id from duel_log_player where duel_log_player.duelLogId = duelLog.id";
const innerQueryParams: any = {};
if(playerName != null && playerName.length) {
//const escapedPlayerName = this.getEscapedString(playerName);
innerQuery += " and duel_log_player.realName = :playerName";
sqb.andWhere('splayer.realName = :playerName');
//innerQuery += " and duel_log_player.realName = :playerName";
innerQueryParams.playerName = playerName;
}
if(playerScore != null && !isNaN(playerScore)) {
innerQuery += " and duel_log_player.score = :playerScore";
//innerQuery += " and duel_log_player.score = :playerScore";
sqb.andWhere('splayer.score = :playerScore');
innerQueryParams.playerScore = playerScore;
}
queryBuilder.andWhere(`exists (${innerQuery})`, innerQueryParams);
queryBuilder.andWhere(`exists ${sqb.getQuery()}`, innerQueryParams);
}
queryBuilder.orderBy("duelLog.id", "DESC")
.leftJoinAndSelect("duelLog.players", "player");
......@@ -322,8 +331,16 @@ export class DataManager {
async getDuelLogFromRecoverSearch(realName: string) {
const repo = this.db.getRepository(DuelLog);
try {
const duelLogs = await repo.createQueryBuilder("duelLog")
.where("startDeckBuffer is not null and currentDeckBuffer is not null and roomMode != 2 and exists (select id from duel_log_player where duel_log_player.duelLogId = duelLog.id and duel_log_player.realName = :realName)", { realName })
const duelLogsQuery = repo.createQueryBuilder("duelLog")
.where('startDeckBuffer is not null')
.andWhere('currentDeckBuffer is not null')
.andWhere('roomMode != 2');
const sqb = duelLogsQuery.subQuery()
.select('splayer.id')
.from(DuelLogPlayer, 'splayer')
.andWhere('splayer.duelLogId = duelLog.id')
.andWhere('splayer.realName = :realName');
const duelLogs = await duelLogsQuery.andWhere(`exists ${sqb.getQuery()}`, { realName })
.orderBy("duelLog.id", "DESC")
.limit(10)
.leftJoinAndSelect("duelLog.players", "player")
......
......@@ -17,7 +17,7 @@ function encodeDeck(deck) {
buffer.writeInt32LE(cardCode, pointer);
pointer += 4;
}
assert_1.default(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
(0, assert_1.default)(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
return buffer;
}
exports.encodeDeck = encodeDeck;
......@@ -28,7 +28,7 @@ function decodeDeck(buffer) {
const sideLength = buffer.readInt32LE(pointer);
pointer += 4;
const correctBufferLength = (2 + mainLength + sideLength) * 4;
assert_1.default(buffer.length >= (2 + mainLength + sideLength) * 4, `Invalid buffer size. Expected: ${correctBufferLength}. Got: ${buffer.length}`);
(0, assert_1.default)(buffer.length >= (2 + mainLength + sideLength) * 4, `Invalid buffer size. Expected: ${correctBufferLength}. Got: ${buffer.length}`);
const main = [];
const side = [];
for (let i = 0; i < mainLength; ++i) {
......
......@@ -13,24 +13,27 @@ exports.Ban = void 0;
const typeorm_1 = require("typeorm");
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let Ban = class Ban extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
id;
ip;
name;
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
(0, typeorm_1.PrimaryGeneratedColumn)({ unsigned: true, type: global.PrimaryKeyType || 'bigint' }),
__metadata("design:type", Number)
], Ban.prototype, "id", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 64, nullable: true }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)({ type: "varchar", length: 64, nullable: true }),
__metadata("design:type", String)
], Ban.prototype, "ip", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20, nullable: true }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)({ type: "varchar", length: 20, nullable: true }),
__metadata("design:type", String)
], Ban.prototype, "name", void 0);
Ban = __decorate([
typeorm_1.Entity(),
typeorm_1.Unique(["ip", "name"])
(0, typeorm_1.Entity)(),
(0, typeorm_1.Unique)(["ip", "name"])
], Ban);
exports.Ban = Ban;
//# sourceMappingURL=Ban.js.map
\ No newline at end of file
......@@ -4,7 +4,7 @@ import {CreateAndUpdateTimeBase} from "./CreateAndUpdateTimeBase";
@Entity()
@Unique(["ip", "name"])
export class Ban extends CreateAndUpdateTimeBase {
@PrimaryGeneratedColumn({ unsigned: true, type: "bigint" })
@PrimaryGeneratedColumn({ unsigned: true, type: (global as any).PrimaryKeyType as ('bigint' | 'integer') || 'bigint' })
id: number;
@Index()
......
......@@ -13,17 +13,20 @@ exports.BasePlayer = void 0;
const typeorm_1 = require("typeorm");
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
class BasePlayer extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
id;
name;
pos;
}
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
(0, typeorm_1.PrimaryGeneratedColumn)({ unsigned: true, type: global.PrimaryKeyType || 'bigint' }),
__metadata("design:type", Number)
], BasePlayer.prototype, "id", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 20 }),
(0, typeorm_1.Column)({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], BasePlayer.prototype, "name", void 0);
__decorate([
typeorm_1.Column({ type: "tinyint" }),
(0, typeorm_1.Column)({ type: "tinyint" }),
__metadata("design:type", Number)
], BasePlayer.prototype, "pos", void 0);
exports.BasePlayer = BasePlayer;
......
import {Column, PrimaryGeneratedColumn} from "typeorm";
import {CreateAndUpdateTimeBase} from "./CreateAndUpdateTimeBase";
export abstract class BasePlayer extends CreateAndUpdateTimeBase {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
@PrimaryGeneratedColumn({unsigned: true, type: (global as any).PrimaryKeyType as ('bigint' | 'integer') || 'bigint'})
id: number;
@Column({ type: "varchar", length: 20 })
......
......@@ -19,15 +19,19 @@ const underscore_1 = __importDefault(require("underscore"));
const moment_1 = __importDefault(require("moment"));
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let CloudReplay = class CloudReplay extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
id;
data;
fromBuffer(buffer) {
this.data = buffer.toString("base64");
}
toBuffer() {
return Buffer.from(this.data, "base64");
}
date;
getDateString() {
return moment_1.default(this.date).format('YYYY-MM-DD HH:mm:ss');
return (0, moment_1.default)(this.date).format('YYYY-MM-DD HH:mm:ss');
}
players;
getPlayerNamesString() {
const playerInfos = underscore_1.default.clone(this.players);
playerInfos.sort((p1, p2) => p1.pos - p2.pos);
......@@ -38,24 +42,24 @@ let CloudReplay = class CloudReplay extends CreateAndUpdateTimeBase_1.CreateAndU
}
};
__decorate([
typeorm_1.PrimaryColumn({ unsigned: true, type: "bigint" }),
(0, typeorm_1.PrimaryColumn)({ unsigned: true, type: global.PrimaryKeyType || 'bigint' }),
__metadata("design:type", Number)
], CloudReplay.prototype, "id", void 0);
__decorate([
typeorm_1.Column({ type: "text" }),
(0, typeorm_1.Column)({ type: "text" }),
__metadata("design:type", String)
], CloudReplay.prototype, "data", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "datetime" }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)({ type: "datetime" }),
__metadata("design:type", Date)
], CloudReplay.prototype, "date", void 0);
__decorate([
typeorm_1.OneToMany(() => CloudReplayPlayer_1.CloudReplayPlayer, player => player.cloudReplay),
(0, typeorm_1.OneToMany)(() => CloudReplayPlayer_1.CloudReplayPlayer, player => player.cloudReplay),
__metadata("design:type", Array)
], CloudReplay.prototype, "players", void 0);
CloudReplay = __decorate([
typeorm_1.Entity({
(0, typeorm_1.Entity)({
orderBy: {
date: "DESC"
}
......
......@@ -10,7 +10,7 @@ import {CreateAndUpdateTimeBase} from "./CreateAndUpdateTimeBase";
}
})
export class CloudReplay extends CreateAndUpdateTimeBase {
@PrimaryColumn({ unsigned: true, type: "bigint" })
@PrimaryColumn({ unsigned: true, type: (global as any).PrimaryKeyType as ('bigint' | 'integer') || 'bigint' })
id: number;
@Column({ type: "text" })
......
......@@ -15,6 +15,8 @@ const typeorm_1 = require("typeorm");
const CloudReplay_1 = require("./CloudReplay");
const BasePlayer_1 = require("./BasePlayer");
let CloudReplayPlayer = CloudReplayPlayer_1 = class CloudReplayPlayer extends BasePlayer_1.BasePlayer {
key;
cloudReplay;
static fromPlayerInfo(info) {
const p = new CloudReplayPlayer_1();
p.key = info.key;
......@@ -24,16 +26,16 @@ let CloudReplayPlayer = CloudReplayPlayer_1 = class CloudReplayPlayer extends Ba
}
};
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 128 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)({ type: "varchar", length: 128 }),
__metadata("design:type", String)
], CloudReplayPlayer.prototype, "key", void 0);
__decorate([
typeorm_1.ManyToOne(() => CloudReplay_1.CloudReplay, replay => replay.players),
(0, typeorm_1.ManyToOne)(() => CloudReplay_1.CloudReplay, replay => replay.players),
__metadata("design:type", CloudReplay_1.CloudReplay)
], CloudReplayPlayer.prototype, "cloudReplay", void 0);
CloudReplayPlayer = CloudReplayPlayer_1 = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], CloudReplayPlayer);
exports.CloudReplayPlayer = CloudReplayPlayer;
//# sourceMappingURL=CloudReplayPlayer.js.map
\ No newline at end of file
......@@ -12,13 +12,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateAndUpdateTimeBase = void 0;
const typeorm_1 = require("typeorm");
class CreateAndUpdateTimeBase {
createTime;
updateTime;
}
__decorate([
typeorm_1.CreateDateColumn(),
(0, typeorm_1.CreateDateColumn)(),
__metadata("design:type", Date)
], CreateAndUpdateTimeBase.prototype, "createTime", void 0);
__decorate([
typeorm_1.UpdateDateColumn(),
(0, typeorm_1.UpdateDateColumn)(),
__metadata("design:type", Date)
], CreateAndUpdateTimeBase.prototype, "updateTime", void 0);
exports.CreateAndUpdateTimeBase = CreateAndUpdateTimeBase;
......
......@@ -19,16 +19,25 @@ const moment_1 = __importDefault(require("moment"));
const underscore_1 = __importDefault(require("underscore"));
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let DuelLog = class DuelLog extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
id;
time;
name;
roomId;
cloudReplayId; // not very needed to become a relation
replayFileName;
roomMode;
duelCount;
players;
getViewString() {
const viewPlayers = underscore_1.default.clone(this.players);
viewPlayers.sort((p1, p2) => p1.pos - p2.pos);
const playerString = viewPlayers[0].realName.split("$")[0] + (viewPlayers[2] ? "+" + viewPlayers[2].realName.split("$")[0] : "") + " VS " + (viewPlayers[1] ? viewPlayers[1].realName.split("$")[0] : "AI") + (viewPlayers[3] ? "+" + viewPlayers[3].realName.split("$")[0] : "");
return `<${this.id}> ${playerString} ${moment_1.default(this.time).format("YYYY-MM-DD HH-mm-ss")}`;
return `<${this.id}> ${playerString} ${(0, moment_1.default)(this.time).format("YYYY-MM-DD HH-mm-ss")}`;
}
getViewJSON(tournamentModeSettings) {
const data = {
id: this.id,
time: moment_1.default(this.time).format("YYYY-MM-DD HH:mm:ss"),
time: (0, moment_1.default)(this.time).format("YYYY-MM-DD HH:mm:ss"),
name: this.name + (tournamentModeSettings.show_info ? " (Duel:" + this.duelCount + ")" : ""),
roomid: this.roomId,
cloud_replay_id: "R#" + this.cloudReplayId,
......@@ -47,46 +56,46 @@ let DuelLog = class DuelLog extends CreateAndUpdateTimeBase_1.CreateAndUpdateTim
}
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
(0, typeorm_1.PrimaryGeneratedColumn)({ unsigned: true, type: global.PrimaryKeyType || 'bigint' }),
__metadata("design:type", Number)
], DuelLog.prototype, "id", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("datetime"),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("datetime"),
__metadata("design:type", Date)
], DuelLog.prototype, "time", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], DuelLog.prototype, "name", void 0);
__decorate([
typeorm_1.Column("int"),
(0, typeorm_1.Column)("int"),
__metadata("design:type", Number)
], DuelLog.prototype, "roomId", void 0);
__decorate([
typeorm_1.Column("bigint"),
(0, typeorm_1.Column)(global.PrimaryKeyType || 'bigint'),
__metadata("design:type", Number)
], DuelLog.prototype, "cloudReplayId", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 256 }),
(0, typeorm_1.Column)({ type: "varchar", length: 256 }),
__metadata("design:type", String)
], DuelLog.prototype, "replayFileName", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
(0, typeorm_1.Column)("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLog.prototype, "roomMode", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("tinyint", { unsigned: true }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLog.prototype, "duelCount", void 0);
__decorate([
typeorm_1.OneToMany(() => DuelLogPlayer_1.DuelLogPlayer, player => player.duelLog),
(0, typeorm_1.OneToMany)(() => DuelLogPlayer_1.DuelLogPlayer, player => player.duelLog),
__metadata("design:type", Array)
], DuelLog.prototype, "players", void 0);
DuelLog = __decorate([
typeorm_1.Entity({
(0, typeorm_1.Entity)({
orderBy: {
id: "DESC"
}
......
......@@ -10,7 +10,7 @@ import {CreateAndUpdateTimeBase} from "./CreateAndUpdateTimeBase";
}
})
export class DuelLog extends CreateAndUpdateTimeBase {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
@PrimaryGeneratedColumn({unsigned: true, type: (global as any).PrimaryKeyType as ('bigint' | 'integer') || 'bigint'})
id: number;
@Index()
......@@ -24,7 +24,7 @@ export class DuelLog extends CreateAndUpdateTimeBase {
@Column("int")
roomId: number;
@Column("bigint")
@Column((global as any).PrimaryKeyType as ('bigint' | 'integer') || 'bigint')
cloudReplayId: number; // not very needed to become a relation
@Column({type: "varchar", length: 256})
......
......@@ -16,26 +16,36 @@ const BasePlayer_1 = require("./BasePlayer");
const DuelLog_1 = require("./DuelLog");
const DeckEncoder_1 = require("../DeckEncoder");
let DuelLogPlayer = DuelLogPlayer_1 = class DuelLogPlayer extends BasePlayer_1.BasePlayer {
realName;
ip;
isFirst;
score;
lp;
cardCount;
startDeckBuffer;
currentDeckBuffer;
winner;
setStartDeck(deck) {
if (deck === null) {
this.startDeckBuffer = null;
return;
}
this.startDeckBuffer = DeckEncoder_1.encodeDeck(deck).toString("base64");
this.startDeckBuffer = (0, DeckEncoder_1.encodeDeck)(deck).toString("base64");
}
getStartDeck() {
return DeckEncoder_1.decodeDeck(Buffer.from(this.startDeckBuffer, "base64"));
return (0, DeckEncoder_1.decodeDeck)(Buffer.from(this.startDeckBuffer, "base64"));
}
setCurrentDeck(deck) {
if (deck === null) {
this.currentDeckBuffer = null;
return;
}
this.currentDeckBuffer = DeckEncoder_1.encodeDeck(deck).toString("base64");
this.currentDeckBuffer = (0, DeckEncoder_1.encodeDeck)(deck).toString("base64");
}
getCurrentDeck() {
return DeckEncoder_1.decodeDeck(Buffer.from(this.currentDeckBuffer, "base64"));
return (0, DeckEncoder_1.decodeDeck)(Buffer.from(this.currentDeckBuffer, "base64"));
}
duelLog;
static fromDuelLogPlayerInfo(info) {
const p = new DuelLogPlayer_1();
p.name = info.name;
......@@ -47,55 +57,55 @@ let DuelLogPlayer = DuelLogPlayer_1 = class DuelLogPlayer extends BasePlayer_1.B
p.cardCount = info.cardCount;
p.isFirst = info.isFirst ? 1 : 0;
p.winner = info.winner ? 1 : 0;
p.startDeckBuffer = info.startDeckBuffer.toString("base64");
p.startDeckBuffer = info.startDeckBuffer?.toString("base64") || null;
p.setCurrentDeck(info.deck);
return p;
}
};
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "realName", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 64, nullable: true }),
(0, typeorm_1.Column)({ type: "varchar", length: 64, nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "ip", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
(0, typeorm_1.Column)("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "isFirst", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("tinyint"),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("tinyint"),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "score", void 0);
__decorate([
typeorm_1.Column("int", { nullable: true }),
(0, typeorm_1.Column)("int", { nullable: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "lp", void 0);
__decorate([
typeorm_1.Column("smallint", { nullable: true }),
(0, typeorm_1.Column)("smallint", { nullable: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "cardCount", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
(0, typeorm_1.Column)("text", { nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "startDeckBuffer", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
(0, typeorm_1.Column)("text", { nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "currentDeckBuffer", void 0);
__decorate([
typeorm_1.Column("tinyint"),
(0, typeorm_1.Column)("tinyint"),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "winner", void 0);
__decorate([
typeorm_1.ManyToOne(() => DuelLog_1.DuelLog, duelLog => duelLog.players),
(0, typeorm_1.ManyToOne)(() => DuelLog_1.DuelLog, duelLog => duelLog.players),
__metadata("design:type", DuelLog_1.DuelLog)
], DuelLogPlayer.prototype, "duelLog", void 0);
DuelLogPlayer = DuelLogPlayer_1 = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], DuelLogPlayer);
exports.DuelLogPlayer = DuelLogPlayer;
//# sourceMappingURL=DuelLogPlayer.js.map
\ No newline at end of file
......@@ -73,7 +73,7 @@ export class DuelLogPlayer extends BasePlayer {
p.cardCount = info.cardCount;
p.isFirst = info.isFirst ? 1 : 0;
p.winner = info.winner ? 1 : 0;
p.startDeckBuffer = info.startDeckBuffer.toString("base64");
p.startDeckBuffer = info.startDeckBuffer?.toString("base64") || null;
p.setCurrentDeck(info.deck);
return p;
}
......
......@@ -13,6 +13,11 @@ exports.RandomDuelBan = void 0;
const typeorm_1 = require("typeorm");
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let RandomDuelBan = class RandomDuelBan extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
ip;
time;
count;
reasons;
needTip;
setNeedTip(need) {
this.needTip = need ? 1 : 0;
}
......@@ -21,27 +26,27 @@ let RandomDuelBan = class RandomDuelBan extends CreateAndUpdateTimeBase_1.Create
}
};
__decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 64 }),
(0, typeorm_1.PrimaryColumn)({ type: "varchar", length: 64 }),
__metadata("design:type", String)
], RandomDuelBan.prototype, "ip", void 0);
__decorate([
typeorm_1.Column("datetime"),
(0, typeorm_1.Column)("datetime"),
__metadata("design:type", Date)
], RandomDuelBan.prototype, "time", void 0);
__decorate([
typeorm_1.Column("smallint"),
(0, typeorm_1.Column)("smallint"),
__metadata("design:type", Number)
], RandomDuelBan.prototype, "count", void 0);
__decorate([
typeorm_1.Column({ type: "simple-array" }),
(0, typeorm_1.Column)({ type: "simple-array" }),
__metadata("design:type", Array)
], RandomDuelBan.prototype, "reasons", void 0);
__decorate([
typeorm_1.Column({ type: "tinyint", unsigned: true }),
(0, typeorm_1.Column)({ type: "tinyint", unsigned: true }),
__metadata("design:type", Number)
], RandomDuelBan.prototype, "needTip", void 0);
RandomDuelBan = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], RandomDuelBan);
exports.RandomDuelBan = RandomDuelBan;
//# sourceMappingURL=RandomDuelBan.js.map
\ No newline at end of file
......@@ -13,6 +13,11 @@ exports.RandomDuelScore = void 0;
const typeorm_1 = require("typeorm");
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let RandomDuelScore = class RandomDuelScore extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
name;
winCount;
loseCount;
fleeCount;
winCombo;
getDisplayName() {
return this.name.split("$")[0];
}
......@@ -44,30 +49,30 @@ let RandomDuelScore = class RandomDuelScore extends CreateAndUpdateTimeBase_1.Cr
}
};
__decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 20 }),
(0, typeorm_1.PrimaryColumn)({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], RandomDuelScore.prototype, "name", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true, default: 0 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "winCount", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true, default: 0 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "loseCount", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true, default: 0 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "fleeCount", void 0);
__decorate([
typeorm_1.Column("int", { unsigned: true, default: 0 }),
(0, typeorm_1.Column)("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "winCombo", void 0);
RandomDuelScore = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], RandomDuelScore);
exports.RandomDuelScore = RandomDuelScore;
//# sourceMappingURL=RandomDuelScore.js.map
\ No newline at end of file
......@@ -19,41 +19,48 @@ const VipKey_1 = require("./VipKey");
const moment_1 = __importDefault(require("moment"));
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let User = class User extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
key;
chatColor;
vipExpireDate;
isVip() {
return this.vipExpireDate && moment_1.default().isBefore(this.vipExpireDate);
return this.vipExpireDate && (0, moment_1.default)().isBefore(this.vipExpireDate);
}
victory;
words;
dialogues;
usedKeys;
};
__decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 128 }),
(0, typeorm_1.PrimaryColumn)({ type: "varchar", length: 128 }),
__metadata("design:type", String)
], User.prototype, "key", void 0);
__decorate([
typeorm_1.Column("varchar", { length: 16, nullable: true }),
(0, typeorm_1.Column)("varchar", { length: 16, nullable: true }),
__metadata("design:type", String)
], User.prototype, "chatColor", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("datetime", { nullable: true }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("datetime", { nullable: true }),
__metadata("design:type", Date)
], User.prototype, "vipExpireDate", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
(0, typeorm_1.Column)("text", { nullable: true }),
__metadata("design:type", String)
], User.prototype, "victory", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
(0, typeorm_1.Column)("text", { nullable: true }),
__metadata("design:type", String)
], User.prototype, "words", void 0);
__decorate([
typeorm_1.OneToMany(() => UserDialog_1.UserDialog, dialog => dialog.user),
(0, typeorm_1.OneToMany)(() => UserDialog_1.UserDialog, dialog => dialog.user),
__metadata("design:type", Array)
], User.prototype, "dialogues", void 0);
__decorate([
typeorm_1.OneToMany(() => VipKey_1.VipKey, vipKey => vipKey.usedBy),
(0, typeorm_1.OneToMany)(() => VipKey_1.VipKey, vipKey => vipKey.usedBy),
__metadata("design:type", Array)
], User.prototype, "usedKeys", void 0);
User = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], User);
exports.User = User;
//# sourceMappingURL=User.js.map
\ No newline at end of file
......@@ -13,26 +13,30 @@ exports.UserDialog = void 0;
const typeorm_1 = require("typeorm");
const User_1 = require("./User");
let UserDialog = class UserDialog {
id;
cardCode;
text;
user;
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
(0, typeorm_1.PrimaryGeneratedColumn)({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], UserDialog.prototype, "id", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("int", { unsigned: true }),
__metadata("design:type", Number)
], UserDialog.prototype, "cardCode", void 0);
__decorate([
typeorm_1.Column("text"),
(0, typeorm_1.Column)("text"),
__metadata("design:type", String)
], UserDialog.prototype, "text", void 0);
__decorate([
typeorm_1.ManyToOne(() => User_1.User, user => user.dialogues),
(0, typeorm_1.ManyToOne)(() => User_1.User, user => user.dialogues),
__metadata("design:type", User_1.User)
], UserDialog.prototype, "user", void 0);
UserDialog = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], UserDialog);
exports.UserDialog = UserDialog;
//# sourceMappingURL=UserDialog.js.map
\ No newline at end of file
......@@ -14,34 +14,39 @@ const typeorm_1 = require("typeorm");
const User_1 = require("./User");
const CreateAndUpdateTimeBase_1 = require("./CreateAndUpdateTimeBase");
let VipKey = class VipKey extends CreateAndUpdateTimeBase_1.CreateAndUpdateTimeBase {
id;
key;
type;
isUsed;
usedBy;
toJSON() {
return { key: this.key, type: this.type };
}
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
(0, typeorm_1.PrimaryGeneratedColumn)({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], VipKey.prototype, "id", void 0);
__decorate([
typeorm_1.Index({ unique: true }),
typeorm_1.Column("varchar", { length: 30 }),
(0, typeorm_1.Index)({ unique: true }),
(0, typeorm_1.Column)("varchar", { length: 30 }),
__metadata("design:type", String)
], VipKey.prototype, "key", void 0);
__decorate([
typeorm_1.Column("int", { unsigned: true }),
(0, typeorm_1.Column)("int", { unsigned: true }),
__metadata("design:type", Number)
], VipKey.prototype, "type", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("tinyint", { unsigned: true, default: 0 }),
(0, typeorm_1.Index)(),
(0, typeorm_1.Column)("tinyint", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], VipKey.prototype, "isUsed", void 0);
__decorate([
typeorm_1.ManyToOne(() => User_1.User, user => user.usedKeys),
(0, typeorm_1.ManyToOne)(() => User_1.User, user => user.usedKeys),
__metadata("design:type", User_1.User)
], VipKey.prototype, "usedBy", void 0);
VipKey = __decorate([
typeorm_1.Entity()
(0, typeorm_1.Entity)()
], VipKey);
exports.VipKey = VipKey;
//# sourceMappingURL=VipKey.js.map
\ No newline at end of file
......@@ -7,7 +7,7 @@
"lflist": 0,
"rule": 0,
"mode": 0,
"comment": "rule: 0=OCGONLY, 1=TCGONLY, 2=OT; mode: 0=SINGLE, 1=MATCH, 2=TAG",
"comment": "rule: 0=OCG-ONLY, 1=TCG-ONLY, 2=SC-ONLY, 3=CUSTOM-ONLY, 4=NO-UNIQUE, 5=ALL; mode: 0=SINGLE, 1=MATCH, 2=TAG",
"duel_rule": 5,
"no_check_deck": false,
"no_shuffle_deck": false,
......@@ -66,12 +66,14 @@
"tips": {
"enabled": true,
"split_zh": false,
"get": "https://api.moecube.com/biu-tips/tips.json",
"get_zh": false
"get": "https://sapi.moecube.com:444/biu-tips/tips.json",
"get_zh": false,
"interval": 30000,
"interval_ingame": 120000
},
"dialogues": {
"enabled": true,
"get_custom": "http://purerosefallen.github.io/ygopro-tips/dialogues-222.json",
"get_custom": "https://minio.momobako.com:9000/public/ygopro-tips/dialogues-222.json",
"get": "http://mercury233.me/ygosrv233/dialogues.json"
},
"words": {
......@@ -160,6 +162,7 @@
"post_detailed_score": true,
"post_score_midduel": true,
"cache_ttl": 60000,
"no_match_mode": false,
"options": {
"apiKey": "123"
},
......@@ -217,6 +220,7 @@
"source": "mycard-athletic"
},
"rankCount": 10,
"banCount": 0,
"ttl": 600
},
"test_mode": {
......
......@@ -85,6 +85,7 @@
"kick_count_down_arena_part2": " will be evicted for not getting ready or starting the game.",
"chat_order_main": "Mycard YGOPro Server function list",
"chat_order_help": "/help show this list",
"chat_order_refresh": "/refresh refresh field when stuck",
"chat_order_roomname": "/roomname show room name",
"chat_order_windbot": "/ai to add an AI, /ai name to add selected AI ",
"chat_order_tip": "/tip show a tip",
......@@ -203,6 +204,10 @@
"auto_death_part2": " minutes.",
"athletic_arena_tip": "During an athletic match, a game quit behavior is regarded as a surrender.",
"windbot_disable_random_room": "By adding the AI, this random game won't get any new player unless they enter the room name:",
"refresh_success": "Refresh field succeeded.",
"refresh_failed": "Refresh field failed.",
"banned_athletic_deck_part1": "Entertainment Mode does not allow top ",
"banned_athletic_deck_part2": " popular meta decks. Please change your deck.",
"using_athletic_deck": " is using a competitive deck."
},
"es-es": {
......@@ -437,6 +442,7 @@
"kick_count_down_arena_part2": "若不准备或开始游戏将视为投降",
"chat_order_main": "Mycard YGOPro Server 指令帮助",
"chat_order_help": "/help 显示这个帮助信息",
"chat_order_refresh": "/refresh 刷新场面信息,卡顿时用",
"chat_order_roomname": "/roomname 显示当前房间的名字",
"chat_order_windbot": "/ai 添加一个AI,/ai 角色名 可指定添加的角色",
"chat_order_tip": "/tip 显示一条提示",
......@@ -555,6 +561,10 @@
"auto_death_part2": "分钟后,将自动进入加时赛。",
"athletic_arena_tip": "在竞技匹配中,比赛开始前退出游戏也会视为投降。",
"windbot_disable_random_room": "因为添加了AI,本随机对战房间将只能通过房间名加入:",
"refresh_success": "刷新场面成功。",
"refresh_fail": "刷新场面失败。",
"banned_athletic_deck_part1": "娱乐匹配中禁止使用使用数前",
"banned_athletic_deck_part2": "的竞技卡组。请更换卡组。",
"using_athletic_deck": " 正在使用竞技卡组。"
},
"ko-kr": {
......
......@@ -2,8 +2,9 @@
"apps": [
{
"name": "ygopro-server",
"script": "/ygopro-server/ygopro-server.js",
"cwd": "/ygopro-server"
"script": "npm",
"cwd": "/ygopro-server",
"args": "start"
},
{
"name": "windbot",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -11,19 +11,15 @@
],
"author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>, Nanahira <78877@qq.com>",
"dependencies": {
"@types/bunyan": "^1.8.6",
"@types/node": "^14.0.13",
"@types/underscore": "^1.10.24",
"async": "^3.2.0",
"axios": "^0.19.2",
"bunyan": "^1.8.14",
"challonge": "latest",
"coffeescript": "^2.5.1",
"deepmerge": "latest",
"formidable": "latest",
"geoip-country-lite": "latest",
"challonge": "^2.2.0",
"deepmerge": "^4.2.2",
"formidable": "^2.0.1",
"geoip-country-lite": "^1.0.0",
"jszip": "^3.5.0",
"load-json-file": "latest",
"load-json-file": "^6.2.0",
"lzma": "^2.3.2",
"moment": "^2.29.1",
"mysql": "^2.18.1",
......@@ -32,21 +28,27 @@
"q": "^1.5.1",
"querystring": "^0.2.0",
"reflect-metadata": "^0.1.13",
"request": "latest",
"sqlite3": "latest",
"request": "^2.88.2",
"sqlite3": "^5.0.2",
"typeorm": "^0.2.29",
"typescript": "^4.0.5",
"underscore": "^1.11.0",
"underscore.string": "latest",
"underscore.string": "^3.3.6",
"ws": "^1.1.1"
},
"license": "AGPL-3.0",
"scripts": {
"build": "coffee -c *.coffee && tsc",
"start": "node ygopro-server.js",
"start": "node ygopro-server.js | bunyan",
"tournament": "node ygopro-tournament.js",
"pre": "node ygopro-pre.js",
"updated": "node ygopro-update.js",
"webhook": "node ygopro-webhook.js"
},
"devDependencies": {
"@types/bunyan": "^1.8.8",
"@types/node": "^17.0.19",
"@types/underscore": "^1.11.4",
"coffeescript": "^2.6.1",
"typescript": "^4.5.5"
}
}
......@@ -18,7 +18,6 @@ _.str = require 'underscore.string'
_.mixin(_.str.exports())
request = require 'request'
axios = require 'axios'
qs = require "querystring"
zlib = require 'zlib'
axios = require 'axios'
......@@ -404,6 +403,7 @@ init = () ->
log.info('Saving migrated settings.')
await setting_save(settings)
if settings.modules.mysql.enabled
global.PrimaryKeyType = if settings.modules.mysql.db.type == 'sqlite' then 'integer' else 'bigint'
DataManager = require('./data-manager/DataManager.js').DataManager
dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log)
log.info('Connecting to database.')
......@@ -492,9 +492,9 @@ init = () ->
badwordR.level3=new RegExp('(?:'+badwords.level3.join(')|(?:')+')','i');
setInterval ()->
moment_now = moment()
moment_now_string = moment_now.format()
moment_long_ago_string = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's').format()
moment_now = global.moment_now = moment()
moment_now_string = global.moment_now_string = moment_now.format()
moment_long_ago_string = global.moment_long_ago_string = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's').format()
return
, 500
......@@ -628,11 +628,18 @@ init = () ->
load_tips_zh()
if settings.modules.tips.enabled
if settings.modules.tips.interval
setInterval ()->
for room in ROOM_all when room and room.established
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING or room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
for room in ROOM_all when room and room.established and room.duel_stage != ygopro.constants.END
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING
return
, settings.modules.tips.interval
if settings.modules.tips.interval_ingame
setInterval ()->
for room in ROOM_all when room and room.established and room.duel_stage != ygopro.constants.END
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage == ygopro.constants.DUEL_STAGE.DUELING
return
, 30000
, settings.modules.tips.interval_ingame
if settings.modules.dialogues.enabled and settings.modules.dialogues.get
load_dialogues()
......@@ -660,9 +667,9 @@ init = () ->
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.random_type and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor(moment_now.diff(room.last_active_time) / 1000)
#log.info time_passed
#log.info time_passed, moment_now_string
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
......@@ -681,7 +688,7 @@ init = () ->
time_passed = Math.floor(moment_now.diff(room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
......@@ -1105,7 +1112,7 @@ CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = (client, deck
if client.system_kicked
return false
disconnect_info = disconnect_list[CLIENT_get_authorize_key(client)]
unless disconnect_info
unless disconnect_info and disconnect_info.deckbuf
return false
room = ROOM_all[disconnect_info.room_id]
if !room
......@@ -1204,7 +1211,7 @@ CLIENT_reconnect = global.CLIENT_reconnect = (client) ->
client.established = true
client.pre_establish_buffers = []
if room.random_type or room.arena
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
CLIENT_import_data(client, dinfo.old_client, room)
CLIENT_send_reconnect_info(client, client.server, room)
#console.log("#{client.name} ${reconnect_to_game}")
......@@ -1233,7 +1240,7 @@ CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = (client, deckbuf) ->
client.established = true
client.pre_establish_buffers = []
if room.random_type or room.arena
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
CLIENT_import_data(client, player, room)
CLIENT_send_reconnect_info(client, client.server, room)
#console.log("#{client.name} ${reconnect_to_game}")
......@@ -1313,23 +1320,7 @@ SOCKET_flush_data = global.SOCKET_flush_data = (sk, datas) ->
return true
getSeedTimet = global.getSeedTimet = (count) ->
ret = []
for i in [0...count]
curTime = null
while !curTime or _.any(ret, (time) ->
return time == curTime.unix()
)
curTime = moment_now
offset = Math.floor(Math.random() * 240) - 120
if offset > 0
curTime = curTime.add(offset, "s")
else if offset < 0
curTime = curTime.subtract(-offset, "s")
ret.push(curTime.unix())
ret.sort((a, b) ->
return a - b
)
return ret
return _.range(count).map(() => 0)
class Room
constructor: (name, @hostinfo) ->
......@@ -1370,15 +1361,15 @@ class Room
@hostinfo.mode = 2
@hostinfo.start_lp = 16000
else if name[0...3] == 'AI#'
@hostinfo.rule = 2
@hostinfo.rule = 5
@hostinfo.lflist = -1
@hostinfo.time_limit = 0
@hostinfo.no_check_deck = true
else if (param = name.match /^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i)
else if (param = name.match /^(\d)(\d)([12345TF])(T|F)(T|F)(\d+),(\d+),(\d+)/i)
@hostinfo.rule = parseInt(param[1])
@hostinfo.mode = parseInt(param[2])
@hostinfo.duel_rule = (if param[3] == 'T' then 3 else 4)
@hostinfo.duel_rule = (if parseInt(param[3]) then parseInt(param[3]) else (if param[3] == 'T' then 3 else 5))
@hostinfo.no_check_deck = param[4] == 'T'
@hostinfo.no_shuffle_deck = param[5] == 'T'
@hostinfo.start_lp = parseInt(param[6])
......@@ -1400,11 +1391,11 @@ class Room
@hostinfo.lflist = 0
if (rule.match /(^|,|,)(OR|OCGRANDOM)(,|,|$)/)
@hostinfo.rule = 2
@hostinfo.rule = 5
@hostinfo.lflist = 0
if (rule.match /(^|,|,)(CR|CCGRANDOM)(,|,|$)/)
@hostinfo.rule = 4
@hostinfo.rule = 2
@hostinfo.lflist = -1
if (rule.match /(^|,|,)(TOR|TCGONLYRANDOM)(,|,|$)/)
......@@ -1412,7 +1403,7 @@ class Room
@hostinfo.lflist = _.findIndex lflists, (list)-> list.tcg
if (rule.match /(^|,|,)(TR|TCGRANDOM)(,|,|$)/)
@hostinfo.rule = 2
@hostinfo.rule = 5
@hostinfo.lflist = _.findIndex lflists, (list)-> list.tcg
if (rule.match /(^|,|,)(OOMR|OCGONLYMATCHRANDOM)(,|,|$)/)
......@@ -1421,12 +1412,12 @@ class Room
@hostinfo.mode = 1
if (rule.match /(^|,|,)(OMR|OCGMATCHRANDOM)(,|,|$)/)
@hostinfo.rule = 2
@hostinfo.rule = 5
@hostinfo.lflist = 0
@hostinfo.mode = 1
if (rule.match /(^|,|,)(CMR|CCGMATCHRANDOM)(,|,|$)/)
@hostinfo.rule = 4
@hostinfo.rule = 2
@hostinfo.lflist = -1
@hostinfo.mode = 1
......@@ -1436,7 +1427,7 @@ class Room
@hostinfo.mode = 1
if (rule.match /(^|,|,)(TMR|TCGMATCHRANDOM)(,|,|$)/)
@hostinfo.rule = 2
@hostinfo.rule = 5
@hostinfo.lflist = _.findIndex lflists, (list)-> list.tcg
@hostinfo.mode = 1
......@@ -1449,10 +1440,10 @@ class Room
@hostinfo.lflist = 0
if (rule.match /(^|,|,)(OT|TCG)(,|,|$)/)
@hostinfo.rule = 2
@hostinfo.rule = 5
if (rule.match /(^|,|,)(CN|CCG|CHINESE)(,|,|$)/)
@hostinfo.rule = 4
if (rule.match /(^|,|,)(SC|CN|CCG|CHINESE)(,|,|$)/)
@hostinfo.rule = 2
@hostinfo.lflist = -1
if (param = rule.match /(^|,|,)LP(\d+)(,|,|$)/)
......@@ -1487,6 +1478,9 @@ class Room
@hostinfo.lflist = -1
if (rule.match /(^|,|,)(NOUNIQUE|NU)(,|,|$)/)
@hostinfo.rule = 4
if (rule.match /(^|,|,)(CUSTOM|DIY)(,|,|$)/)
@hostinfo.rule = 3
if (rule.match /(^|,|,)(NOCHECK|NC)(,|,|$)/)
......@@ -1832,7 +1826,7 @@ class Room
@finished = true
if !@finished_by_death
@scores[client.name_vpass] = -9
if @random_type and not client.flee_free and (!settings.modules.reconnect.enabled or @get_disconnected_count() == 0)
if @random_type and not client.flee_free and (!settings.modules.reconnect.enabled or @get_disconnected_count() == 0) and not client.kicked_by_system and not client.kicked_by_player
ROOM_ban_player(client.name, client.ip, "${random_ban_reason_flee}")
if settings.modules.random_duel.record_match_scores and @random_type == 'M'
ROOM_player_flee(client.name_vpass)
......@@ -1981,6 +1975,12 @@ class Room
for line in _.lines lines
ygopro.stoc_send_chat_to_room(this, line, ygopro.constants.COLORS.PINK)
refreshLastActiveTime: (longAgo) ->
if longAgo
@last_active_time = moment_long_ago_string
else
@last_active_time = moment_now_string
# 网络连接
netRequestHandler = (client) ->
client.ip = client.remoteAddress
......@@ -1992,6 +1992,11 @@ netRequestHandler = (client) ->
ROOM_connected_ip[client.ip] = connect_count
#log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
client.destroy()
return
# server stand for the connection to ygopro server process
server = new net.Socket()
client.server = server
......@@ -2073,11 +2078,6 @@ netRequestHandler = (client) ->
SERVER_clear_disconnect(server)
return
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
CLIENT_kick(client)
return
client.playLines = (lines) ->
for line in _.lines lines
ygopro.stoc_send_chat(client, line, ygopro.constants.COLORS.PINK)
......@@ -2126,14 +2126,20 @@ netRequestHandler = (client) ->
return
room.watcher.write(buffer) for buffer in handle_data.datas
else
ctos_filter = if settings.modules.reconnect.enabled and client.pre_reconnecting then ["UPDATE_DECK"] else null
ctos_filter = null
preconnect = false
if settings.modules.reconnect.enabled and client.pre_reconnecting_to_room
ctos_filter = ["UPDATE_DECK"]
if client.name == null
ctos_filter = ["JOIN_GAME", "PLAYER_INFO"]
preconnect = true
handle_data = await ygopro.helper.handleBuffer(ctos_buffer, "CTOS", ctos_filter, {
client: client,
server: client.server
})
}, preconnect)
if handle_data.feedback
log.warn(handle_data.feedback.message, client.name, client.ip)
if handle_data.feedback.type == "OVERSIZE" or ROOM_bad_ip[client.ip] > 5
if handle_data.feedback.type == "OVERSIZE" or handle_data.feedback.type == "INVALID_PACKET" or ROOM_bad_ip[client.ip] > 5
bad_ip_count = ROOM_bad_ip[client.ip]
if bad_ip_count
ROOM_bad_ip[client.ip] = bad_ip_count + 1
......@@ -2141,7 +2147,7 @@ netRequestHandler = (client) ->
ROOM_bad_ip[client.ip] = 1
CLIENT_kick(client)
return
if !client.server
if client.closed || !client.server
return
if client.established
client.server.write buffer for buffer in handle_data.datas
......@@ -2177,6 +2183,11 @@ deck_name_match = global.deck_name_match = (deck_name, player_name) ->
# return true to cancel a synchronous message
ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)->
# second PLAYER_INFO = attack
if client.name
log.info 'DUP PLAYER_INFO', client.ip
CLIENT_kick client
return '_cancel'
# checkmate use username$password, but here don't
# so remove the password
name_full =info.name.replace(/\\/g, "").split("$")
......@@ -2193,18 +2204,6 @@ ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)->
return false
, name))
client.rag = true
if settings.modules.mycard.enabled and settings.modules.mycard.ban_get and !client.is_local
try
banMCRequest = await axios.get settings.modules.mycard.ban_get,
paramsSerializer: qs.stringify
params:
user: name
if typeof(banMCRequest.data) == "object"
client.ban_mc = banMCRequest.data
else
log.warn "ban get bad json", banMCRequest.data
catch e
log.warn 'ban get error', e.toString()
struct = ygopro.structs.get("CTOS_PlayerInfo")
struct._setBuff(buffer)
struct.set("name", name)
......@@ -2315,13 +2314,26 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
ygopro.stoc_die(client, '${invalid_password_payload}')
return
if settings.modules.mycard.enabled and settings.modules.mycard.ban_get and !client.is_local
axios.get settings.modules.mycard.ban_get,
paramsSerializer: qs.stringify
params:
user: client.name
.then (banMCRequest) ->
if typeof(banMCRequest.data) == "object"
client.ban_mc = banMCRequest.data
else
log.warn "ban get bad json", banMCRequest.data
.catch (e) ->
log.warn 'ban get error', e.toString()
check_buffer_indentity = (buf)->
checksum = 0
for i in [0...buf.length]
checksum += buf.readUInt8(i)
(checksum & 0xFF) == 0
create_room_with_action = (buffer, decrypted_buffer, match_permit)->
create_room_with_action = (buffer, decrypted_buffer)->
if client.closed
return
firstByte = buffer.readUInt8(1)
......@@ -2349,9 +2361,9 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
options = {
lflist: settings.hostinfo.lflist
time_limit: settings.hostinfo.time_limit
rule: (opt1 >> 5) & 0x7 # 0 1 2 3 4
rule: (opt1 >> 5) & 0x7 # 0 1 2 3 4 5
mode: (opt1 >> 3) & 0x3 # 0 1 2
duel_rule: opt0 >> 1 # 1 2 3 4 5
duel_rule: (opt0 >> 1) || 5 # 1 2 3 4 5
no_check_deck: !!((opt1 >> 1) & 1)
no_shuffle_deck: !!(opt1 & 1)
start_lp: opt2
......@@ -2361,6 +2373,9 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
auto_death: !!(opt0 & 0x1) ? 40 : false
}
#console.log(options)
if options.rule == 2
options.lflist = -1
else if options.rule != 3
options.lflist = _.findIndex lflists, (list)-> ((options.rule == 1) == list.tcg) and list.date.isBefore()
room_title = info.pass.slice(8).replace(String.fromCharCode(0xFEFF), ' ')
if badwordR.level3.test(room_title)
......@@ -2386,7 +2401,21 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
ygopro.stoc_die(client, '${invalid_password_not_found}')
return
when 4
if match_permit and !match_permit.permit
if settings.modules.arena_mode.check_permit
try
matchPermitRes = await axios.get settings.modules.arena_mode.check_permit,
responseType: 'json'
timeout: 3000
params:
username: client.name,
password: info.pass,
arena: settings.modules.arena_mode.mode
match_permit = matchPermitRes.data
catch e
log.warn "match permit fail #{e.toString()}"
if client.closed
return
if match_permit and match_permit.permit == false
ygopro.stoc_die(client, '${invalid_password_unauthorized}')
return
room = await ROOM_find_or_create_by_name('M#' + info.pass.slice(8))
......@@ -2417,93 +2446,46 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
room.join_player(client)
return
_async.auto({
match_permit: (done) ->
if client.closed
done()
return
if(!settings.modules.arena_mode.check_permit)
done(null, null)
return
request
url: settings.modules.arena_mode.check_permit,
json: true,
qs:
username: client.name,
password: info.pass,
arena: settings.modules.arena_mode.mode
, (error, response, body)->
if client.closed
done(null, null)
return
if !error and body
done(null, body)
else
log.warn("Match permit request error", error)
done(null, null)
return
return
get_user: (done) ->
if client.closed
done()
return
decrypted_buffer = null
if id = users_cache[client.name]
secret = id % 65535 + 1
decrypted_buffer = Buffer.allocUnsafe(6)
for i in [0, 2, 4]
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i)
if check_buffer_indentity(decrypted_buffer)
done(null, {
original: decrypted_buffer,
decrypted: decrypted_buffer
})
return
return create_room_with_action(decrypted_buffer, decrypted_buffer)
#TODO: query database directly, like preload.
request
baseUrl: settings.modules.mycard.auth_base_url,
url: '/users/' + encodeURIComponent(client.name) + '.json',
qs:
try
userUrl = "#{settings.modules.mycard.auth_base_url}/users/#{encodeURIComponent(client.name)}.json"
#console.log(userUrl)
userDataRes = await axios.get userUrl,
responseType: 'json'
timeout: 4000
params:
api_key: settings.modules.mycard.auth_key,
api_username: client.name,
skip_track_visit: true
json: true
, (error, response, body)->
if !error and body and body.user
users_cache[client.name] = body.user.id
secret = body.user.id % 65535 + 1
userData = userDataRes.data
#console.log userData
catch e
log.warn("READ USER FAIL", client.name, e.toString())
if !client.closed
ygopro.stoc_die(client, '${load_user_info_fail}')
return
if client.closed
return
users_cache[client.name] = userData.user.id
secret = userData.user.id % 65535 + 1
decrypted_buffer = Buffer.allocUnsafe(6)
for i in [0, 2, 4]
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i)
if check_buffer_indentity(decrypted_buffer)
buffer = decrypted_buffer
else
log.warn("READ USER FAIL", client.name, error, body)
done("${load_user_info_fail}")
return
# buffer != decrypted_buffer ==> auth failed
if !check_buffer_indentity(buffer)
done('${invalid_password_checksum}')
ygopro.stoc_die(client, '${invalid_password_checksum}')
return
done(null, {
original: buffer,
decrypted: decrypted_buffer
})
return
return
}, (err, data) ->
if(client.closed)
return
if(err)
ygopro.stoc_die(client, err)
return
create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit)
)
return create_room_with_action(buffer, decrypted_buffer)
else if settings.modules.challonge.enabled
if info.version != settings.version and settings.alternative_versions.includes(info.version)
......@@ -2562,9 +2544,13 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
#if found.winnerId
# ygopro.stoc_die(client, '${challonge_match_already_finished}')
# return
create_room_name = 'M#' + found.id
create_room_name = found.id.toString()
if !settings.modules.challonge.no_match_mode
create_room_name = 'M#' + create_room_name
if recover_match
create_room_name = recover_match[0] + ',' + create_room_name
else if recover_match
create_room_name = recover_match[0] + '#' + create_room_name
room = await ROOM_find_or_create_by_name(create_room_name)
if room
room.challonge_info = found
......@@ -2757,7 +2743,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
return true
else
room.waiting_for_player = client
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
#log.info("#{msg_name}等待#{room.waiting_for_player.name}")
#log.info 'MSG', msg_name
......@@ -2873,6 +2859,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
pos = 1 - pos unless client.is_first
pos = pos * 2 if pos >= 0 and room.hostinfo.mode == 2
val = buffer.readInt32LE(2)
if room.dueling_players[pos]
room.dueling_players[pos].lp -= val
room.dueling_players[pos].lp = 0 if room.dueling_players[pos].lp < 0
if 0 < room.dueling_players[pos].lp <= 100
......@@ -2883,6 +2870,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
pos = 1 - pos unless client.is_first
pos = pos * 2 if pos >= 0 and room.hostinfo.mode == 2
val = buffer.readInt32LE(2)
if room.dueling_players[pos]
room.dueling_players[pos].lp += val
if msg_name == 'LPUPDATE' and client.pos == 0
......@@ -2890,6 +2878,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
pos = 1 - pos unless client.is_first
pos = pos * 2 if pos >= 0 and room.hostinfo.mode == 2
val = buffer.readInt32LE(2)
if room.dueling_players[pos]
room.dueling_players[pos].lp = val
if msg_name == 'PAY_LPCOST' and client.pos == 0
......@@ -2897,6 +2886,7 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
pos = 1 - pos unless client.is_first
pos = pos * 2 if pos >= 0 and room.hostinfo.mode == 2
val = buffer.readInt32LE(2)
if room.dueling_players[pos]
room.dueling_players[pos].lp -= val
room.dueling_players[pos].lp = 0 if room.dueling_players[pos].lp < 0
if 0 < room.dueling_players[pos].lp <= 100
......@@ -3103,6 +3093,7 @@ ygopro.stoc_follow 'HS_PLAYER_CHANGE', true, (buffer, info, client, server, data
if room.waiting_for_player != room.waiting_for_player2
room.waiting_for_player2 = room.waiting_for_player
room.waiting_for_player_time = settings.modules.arena_mode.ready_time
if !room.waiting_for_player_interval
room.waiting_for_player_interval = setInterval (()-> wait_room_start_arena(ROOM_all[client.rid]);return), 1000
else if !room.waiting_for_player and room.waiting_for_player_interval
clearInterval room.waiting_for_player_interval
......@@ -3144,6 +3135,7 @@ ygopro.stoc_follow 'DUEL_END', false, (buffer, info, client, server, datas)->
wait_room_start = (room, time)->
if room and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.ready_player_count_without_host >= room.max_player - 1
#log.info('wait room start', time)
time -= 1
if time
unless time % 5
......@@ -3335,7 +3327,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
return unless room
msg = _.trim(info.msg)
cancel = _.startsWith(msg, "/")
room.last_active_time = moment_now_string unless cancel or not (room.random_type or room.arena) or room.duel_stage == ygopro.constants.DUEL_STAGE.FINGER or room.duel_stage == ygopro.constants.DUEL_STAGE.FIRSTGO or room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING
room.refreshLastActiveTime() unless cancel or not (room.random_type or room.arena) or room.duel_stage == ygopro.constants.DUEL_STAGE.FINGER or room.duel_stage == ygopro.constants.DUEL_STAGE.FIRSTGO or room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING
cmd = msg.split(' ')
isVip = await CLIENT_check_vip(client)
switch cmd[0]
......@@ -3349,7 +3341,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
ygopro.ctos_send(client.server, 'SURRENDER')
else
sur_player = CLIENT_get_partner(client)
if sur_player.closed or sur_player.is_local
if !sur_player or sur_player.closed or sur_player.is_local
sur_player = client
if room.hostinfo.mode==2 and sur_player != client
ygopro.stoc_send_chat(sur_player, "${surrender_confirm_tag}", ygopro.constants.COLORS.BABYBLUE)
......@@ -3362,6 +3354,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
ygopro.stoc_send_chat(client, "${chat_order_main}")
ygopro.stoc_send_chat(client, "${chat_order_help}")
ygopro.stoc_send_chat(client, "${chat_order_refresh}")
ygopro.stoc_send_chat(client, "${chat_order_roomname}") if !settings.modules.mycard.enabled
ygopro.stoc_send_chat(client, "${chat_order_windbot}") if settings.modules.windbot.enabled
ygopro.stoc_send_chat(client, "${chat_order_tip}") if settings.modules.tips.enabled
......@@ -3391,6 +3384,15 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
when '/roomname'
ygopro.stoc_send_chat(client, "${room_name} " + room.name, ygopro.constants.COLORS.BABYBLUE) if room
when '/refresh'
if room.duel_stage == ygopro.constants.DUEL_STAGE.DUELING and client.last_game_msg and 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)
ygopro.stoc_send_chat(client, '${refresh_success}', ygopro.constants.COLORS.BABYBLUE)
else
ygopro.stoc_send_chat(client, '${refresh_fail}', ygopro.constants.COLORS.RED)
when '/color'
if settings.modules.chat_color.enabled
cip = CLIENT_get_authorize_key(client)
......@@ -3501,7 +3503,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
client.abuse_count=client.abuse_count+2 if client.abuse_count
ygopro.stoc_send_chat(client, "${chat_warn_level0}", ygopro.constants.COLORS.RED)
cancel = true
if !(room and (room.random_type or room.arena))
if !(room and (room.random_type or room.arena)) and not settings.modules.mycard.enabled
if !cancel and settings.modules.display_watchers and client.is_post_watcher
ygopro.stoc_send_chat_to_room(room, "#{client.name}: #{msg}", 9)
return true
......@@ -3615,7 +3617,7 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
if room.random_type or room.arena
if client.pos == 0
room.waiting_for_player = room.waiting_for_player2
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering
recover_player_data = _.find(room.recover_duel_log.players, (player) ->
return player.realName == client.name_vpass and buffer.compare(Buffer.from(player.startDeckBuffer, "base64")) == 0
......@@ -3632,7 +3634,20 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED)
else if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.deck_check
return false
else
if room.arena and settings.modules.athletic_check.enabled and settings.modules.athletic_check.banCount
athleticCheckResult = await athleticChecker.checkAthletic({main: buff_main, side: buff_side})
if athleticCheckResult.success
if athleticCheckResult.athletic and athleticCheckResult.athletic <= settings.modules.athletic_check.banCount
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
ygopro.stoc_send_chat(client, "${banned_athletic_deck_part1}#{settings.modules.athletic_check.banCount}${banned_athletic_deck_part2}", ygopro.constants.COLORS.RED)
return false
else
log.warn("GET ATHLETIC FAIL", client.name, athleticCheckResult.message)
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.deck_check
decks = await fs.promises.readdir(settings.modules.tournament_mode.deck_path)
if decks.length
struct.set("mainc", 1)
......@@ -3665,15 +3680,17 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
else
#log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
ygopro.stoc_send_chat(client, "${deck_incorrect_part1} #{found_deck} ${deck_incorrect_part2}", ygopro.constants.COLORS.RED)
return false
else
#log.info("player deck not found: " + client.name)
ygopro.stoc_send_chat(client, "#{client.name}${deck_not_found}", ygopro.constants.COLORS.RED)
return false
await return false
ygopro.ctos_follow 'RESPONSE', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
return unless room and (room.random_type or room.arena)
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
await return
ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)->
......@@ -3742,7 +3759,7 @@ ygopro.ctos_follow 'HAND_RESULT', false, (buffer, info, client, server, datas)->
if room.random_type or room.arena
if client.pos == 0
room.waiting_for_player = room.waiting_for_player2
room.last_active_time = moment_long_ago_string
room.refreshLastActiveTime(true)
await return
ygopro.ctos_follow 'TP_RESULT', false, (buffer, info, client, server, datas)->
......@@ -3751,7 +3768,7 @@ ygopro.ctos_follow 'TP_RESULT', false, (buffer, info, client, server, datas)->
client.selected_preduel = true
# room.selecting_tp = false
return unless room.random_type or room.arena
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
await return
ygopro.stoc_follow 'CHAT', true, (buffer, info, client, server, datas)->
......@@ -3790,7 +3807,7 @@ ygopro.stoc_follow 'SELECT_HAND', true, (buffer, info, client, server, datas)->
room.waiting_for_player = client
else
room.waiting_for_player2 = client
room.last_active_time = moment_long_ago_string
room.refreshLastActiveTime(true)
if room.determine_firstgo
ygopro.ctos_send(server, "HAND_RESULT", {
res: if client.pos == 0 then 2 else 1
......@@ -3811,7 +3828,7 @@ ygopro.stoc_follow 'SELECT_TP', true, (buffer, info, client, server, datas)->
room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO
if room.random_type or room.arena
room.waiting_for_player = client
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
if room.determine_firstgo
ygopro.ctos_send(server, "TP_RESULT", {
res: if room.determine_firstgo == client then 1 else 0
......@@ -3867,7 +3884,7 @@ ygopro.stoc_follow 'CHANGE_SIDE', false, (buffer, info, client, server, datas)->
room.waiting_for_player = client
else
room.waiting_for_player2 = client
room.last_active_time = moment_now_string
room.refreshLastActiveTime()
await return
ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
......
......@@ -36,8 +36,6 @@
request = require('request');
axios = require('axios');
qs = require("querystring");
zlib = require('zlib');
......@@ -520,6 +518,7 @@
await setting_save(settings);
}
if (settings.modules.mysql.enabled) {
global.PrimaryKeyType = settings.modules.mysql.db.type === 'sqlite' ? 'integer' : 'bigint';
DataManager = require('./data-manager/DataManager.js').DataManager;
dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log);
log.info('Connecting to database.');
......@@ -624,9 +623,9 @@
badwordR.level2 = new RegExp('(?:' + badwords.level2.join(')|(?:') + ')', 'i');
badwordR.level3 = new RegExp('(?:' + badwords.level3.join(')|(?:') + ')', 'i');
setInterval(function() {
moment_now = moment();
moment_now_string = moment_now.format();
moment_long_ago_string = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's').format();
moment_now = global.moment_now = moment();
moment_now_string = global.moment_now_string = moment_now.format();
moment_long_ago_string = global.moment_long_ago_string = moment().subtract(settings.modules.random_duel.hang_timeout - 19, 's').format();
}, 500);
if (settings.modules.max_rooms_count) {
rooms_count = 0;
......@@ -787,17 +786,32 @@
load_tips_zh();
}
if (settings.modules.tips.enabled) {
if (settings.modules.tips.interval) {
setInterval(function() {
var l, len1, room;
for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
room = ROOM_all[l];
if (room && room.established) {
if (room.duel_stage === ygopro.constants.DUEL_STAGE.SIDING || room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
if (room && room.established && room.duel_stage !== ygopro.constants.END) {
if (room.duel_stage !== ygopro.constants.DUEL_STAGE.DUELING) {
ygopro.stoc_send_random_tip_to_room(room);
}
}
}
}, settings.modules.tips.interval);
}
if (settings.modules.tips.interval_ingame) {
setInterval(function() {
var l, len1, room;
for (l = 0, len1 = ROOM_all.length; l < len1; l++) {
room = ROOM_all[l];
if (room && room.established && room.duel_stage !== ygopro.constants.END) {
if (room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING) {
ygopro.stoc_send_random_tip_to_room(room);
}
}
}
}, 30000);
}, settings.modules.tips.interval_ingame);
}
}
if (settings.modules.dialogues.enabled && settings.modules.dialogues.get) {
load_dialogues();
......@@ -832,9 +846,9 @@
continue;
}
time_passed = Math.floor(moment_now.diff(room.last_active_time) / 1000);
//log.info time_passed
//log.info time_passed, moment_now_string
if (time_passed >= settings.modules.random_duel.hang_timeout) {
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}");
room.scores[room.waiting_for_player.name_vpass] = -9;
//log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
......@@ -859,7 +873,7 @@
time_passed = Math.floor(moment_now.diff(room.last_active_time) / 1000);
//log.info time_passed
if (time_passed >= settings.modules.random_duel.hang_timeout) {
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
ygopro.stoc_send_chat_to_room(room, `${room.waiting_for_player.name} \${kicked_by_system}`, ygopro.constants.COLORS.RED);
room.scores[room.waiting_for_player.name_vpass] = -9;
//log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
......@@ -1449,7 +1463,7 @@
return false;
}
disconnect_info = disconnect_list[CLIENT_get_authorize_key(client)];
if (!disconnect_info) {
if (!(disconnect_info && disconnect_info.deckbuf)) {
return false;
}
room = ROOM_all[disconnect_info.room_id];
......@@ -1579,7 +1593,7 @@
client.established = true;
client.pre_establish_buffers = [];
if (room.random_type || room.arena) {
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
CLIENT_import_data(client, dinfo.old_client, room);
CLIENT_send_reconnect_info(client, client.server, room);
......@@ -1611,7 +1625,7 @@
client.established = true;
client.pre_establish_buffers = [];
if (room.random_type || room.arena) {
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
CLIENT_import_data(client, player, room);
CLIENT_send_reconnect_info(client, client.server, room);
......@@ -1720,27 +1734,9 @@
};
getSeedTimet = global.getSeedTimet = function(count) {
var curTime, i, j, offset, ref, ret;
ret = [];
for (i = j = 0, ref = count; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
curTime = null;
while (!curTime || _.any(ret, function(time) {
return time === curTime.unix();
})) {
curTime = moment_now;
offset = Math.floor(Math.random() * 240) - 120;
if (offset > 0) {
curTime = curTime.add(offset, "s");
} else if (offset < 0) {
curTime = curTime.subtract(-offset, "s");
}
}
ret.push(curTime.unix());
}
ret.sort(function(a, b) {
return a - b;
return _.range(count).map(() => {
return 0;
});
return ret;
};
Room = class Room {
......@@ -1786,14 +1782,14 @@
this.hostinfo.mode = 2;
this.hostinfo.start_lp = 16000;
} else if (name.slice(0, 3) === 'AI#') {
this.hostinfo.rule = 2;
this.hostinfo.rule = 5;
this.hostinfo.lflist = -1;
this.hostinfo.time_limit = 0;
this.hostinfo.no_check_deck = true;
} else if ((param = name.match(/^(\d)(\d)(T|F)(T|F)(T|F)(\d+),(\d+),(\d+)/i))) {
} else if ((param = name.match(/^(\d)(\d)([12345TF])(T|F)(T|F)(\d+),(\d+),(\d+)/i))) {
this.hostinfo.rule = parseInt(param[1]);
this.hostinfo.mode = parseInt(param[2]);
this.hostinfo.duel_rule = (param[3] === 'T' ? 3 : 4);
this.hostinfo.duel_rule = (parseInt(param[3]) ? parseInt(param[3]) : (param[3] === 'T' ? 3 : 5));
this.hostinfo.no_check_deck = param[4] === 'T';
this.hostinfo.no_shuffle_deck = param[5] === 'T';
this.hostinfo.start_lp = parseInt(param[6]);
......@@ -1813,11 +1809,11 @@
this.hostinfo.lflist = 0;
}
if (rule.match(/(^|,|,)(OR|OCGRANDOM)(,|,|$)/)) {
this.hostinfo.rule = 2;
this.hostinfo.rule = 5;
this.hostinfo.lflist = 0;
}
if (rule.match(/(^|,|,)(CR|CCGRANDOM)(,|,|$)/)) {
this.hostinfo.rule = 4;
this.hostinfo.rule = 2;
this.hostinfo.lflist = -1;
}
if (rule.match(/(^|,|,)(TOR|TCGONLYRANDOM)(,|,|$)/)) {
......@@ -1827,7 +1823,7 @@
});
}
if (rule.match(/(^|,|,)(TR|TCGRANDOM)(,|,|$)/)) {
this.hostinfo.rule = 2;
this.hostinfo.rule = 5;
this.hostinfo.lflist = _.findIndex(lflists, function(list) {
return list.tcg;
});
......@@ -1838,12 +1834,12 @@
this.hostinfo.mode = 1;
}
if (rule.match(/(^|,|,)(OMR|OCGMATCHRANDOM)(,|,|$)/)) {
this.hostinfo.rule = 2;
this.hostinfo.rule = 5;
this.hostinfo.lflist = 0;
this.hostinfo.mode = 1;
}
if (rule.match(/(^|,|,)(CMR|CCGMATCHRANDOM)(,|,|$)/)) {
this.hostinfo.rule = 4;
this.hostinfo.rule = 2;
this.hostinfo.lflist = -1;
this.hostinfo.mode = 1;
}
......@@ -1855,7 +1851,7 @@
this.hostinfo.mode = 1;
}
if (rule.match(/(^|,|,)(TMR|TCGMATCHRANDOM)(,|,|$)/)) {
this.hostinfo.rule = 2;
this.hostinfo.rule = 5;
this.hostinfo.lflist = _.findIndex(lflists, function(list) {
return list.tcg;
});
......@@ -1872,10 +1868,10 @@
this.hostinfo.lflist = 0;
}
if (rule.match(/(^|,|,)(OT|TCG)(,|,|$)/)) {
this.hostinfo.rule = 2;
this.hostinfo.rule = 5;
}
if (rule.match(/(^|,|,)(CN|CCG|CHINESE)(,|,|$)/)) {
this.hostinfo.rule = 4;
if (rule.match(/(^|,|,)(SC|CN|CCG|CHINESE)(,|,|$)/)) {
this.hostinfo.rule = 2;
this.hostinfo.lflist = -1;
}
if ((param = rule.match(/(^|,|,)LP(\d+)(,|,|$)/))) {
......@@ -1926,6 +1922,9 @@
this.hostinfo.lflist = -1;
}
if (rule.match(/(^|,|,)(NOUNIQUE|NU)(,|,|$)/)) {
this.hostinfo.rule = 4;
}
if (rule.match(/(^|,|,)(CUSTOM|DIY)(,|,|$)/)) {
this.hostinfo.rule = 3;
}
if (rule.match(/(^|,|,)(NOCHECK|NC)(,|,|$)/)) {
......@@ -2419,7 +2418,7 @@
this.finished = true;
if (!this.finished_by_death) {
this.scores[client.name_vpass] = -9;
if (this.random_type && !client.flee_free && (!settings.modules.reconnect.enabled || this.get_disconnected_count() === 0)) {
if (this.random_type && !client.flee_free && (!settings.modules.reconnect.enabled || this.get_disconnected_count() === 0) && !client.kicked_by_system && !client.kicked_by_player) {
ROOM_ban_player(client.name, client.ip, "${random_ban_reason_flee}");
if (settings.modules.random_duel.record_match_scores && this.random_type === 'M') {
ROOM_player_flee(client.name_vpass);
......@@ -2653,6 +2652,14 @@
return results;
}
refreshLastActiveTime(longAgo) {
if (longAgo) {
return this.last_active_time = moment_long_ago_string;
} else {
return this.last_active_time = moment_now_string;
}
}
};
// 网络连接
......@@ -2666,7 +2673,11 @@
}
ROOM_connected_ip[client.ip] = connect_count;
//log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
client.destroy();
return;
}
// server stand for the connection to ygopro server process
server = new net.Socket();
client.server = server;
......@@ -2766,11 +2777,6 @@
SERVER_clear_disconnect(server);
}
});
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
CLIENT_kick(client);
return;
}
client.playLines = function(lines) {
var j, len, line, ref, results;
ref = _.lines(lines);
......@@ -2808,7 +2814,7 @@
// 客户端到服务端(ctos)协议分析
client.pre_establish_buffers = new Array();
client.on('data', async function(ctos_buffer) {
var bad_ip_count, buffer, ctos_filter, handle_data, j, l, len, len1, len2, m, ref, ref1, ref2, room;
var bad_ip_count, buffer, ctos_filter, handle_data, j, l, len, len1, len2, m, preconnect, ref, ref1, ref2, room;
if (client.is_post_watcher) {
room = ROOM_all[client.rid];
if (room) {
......@@ -2836,14 +2842,22 @@
}
}
} else {
ctos_filter = settings.modules.reconnect.enabled && client.pre_reconnecting ? ["UPDATE_DECK"] : null;
ctos_filter = null;
preconnect = false;
if (settings.modules.reconnect.enabled && client.pre_reconnecting_to_room) {
ctos_filter = ["UPDATE_DECK"];
}
if (client.name === null) {
ctos_filter = ["JOIN_GAME", "PLAYER_INFO"];
preconnect = true;
}
handle_data = (await ygopro.helper.handleBuffer(ctos_buffer, "CTOS", ctos_filter, {
client: client,
server: client.server
}));
}, preconnect));
if (handle_data.feedback) {
log.warn(handle_data.feedback.message, client.name, client.ip);
if (handle_data.feedback.type === "OVERSIZE" || ROOM_bad_ip[client.ip] > 5) {
if (handle_data.feedback.type === "OVERSIZE" || handle_data.feedback.type === "INVALID_PACKET" || ROOM_bad_ip[client.ip] > 5) {
bad_ip_count = ROOM_bad_ip[client.ip];
if (bad_ip_count) {
ROOM_bad_ip[client.ip] = bad_ip_count + 1;
......@@ -2854,7 +2868,7 @@
return;
}
}
if (!client.server) {
if (client.closed || !client.server) {
return;
}
if (client.established) {
......@@ -2908,7 +2922,13 @@
// 功能模块
// return true to cancel a synchronous message
ygopro.ctos_follow('PLAYER_INFO', true, async function(buffer, info, client, server, datas) {
var banMCRequest, e, geo, lang, name, name_full, struct, vpass;
var geo, lang, name, name_full, struct, vpass;
// second PLAYER_INFO = attack
if (client.name) {
log.info('DUP PLAYER_INFO', client.ip);
CLIENT_kick(client);
return '_cancel';
}
// checkmate use username$password, but here don't
// so remove the password
name_full = info.name.replace(/\\/g, "").split("$");
......@@ -2929,24 +2949,6 @@
}, name)) {
client.rag = true;
}
if (settings.modules.mycard.enabled && settings.modules.mycard.ban_get && !client.is_local) {
try {
banMCRequest = (await axios.get(settings.modules.mycard.ban_get, {
paramsSerializer: qs.stringify,
params: {
user: name
}
}));
if (typeof banMCRequest.data === "object") {
client.ban_mc = banMCRequest.data;
} else {
log.warn("ban get bad json", banMCRequest.data);
}
} catch (error1) {
e = error1;
log.warn('ban get error', e.toString());
}
}
struct = ygopro.structs.get("CTOS_PlayerInfo");
struct._setBuff(buffer);
struct.set("name", name);
......@@ -2975,7 +2977,7 @@
});
ygopro.ctos_follow('JOIN_GAME', true, async function(buffer, info, client, server, datas) {
var available_logs, check_buffer_indentity, create_room_with_action, duelLog, exactBan, index, j, l, len, len1, pre_room, recover_match, replay, replay_id, replays, room, struct;
var available_logs, check_buffer_indentity, create_room_with_action, decrypted_buffer, duelLog, e, exactBan, i, id, index, j, l, len, len1, len2, len3, m, n, pre_room, recover_match, ref, ref1, replay, replay_id, replays, room, secret, struct, userData, userDataRes, userUrl;
//log.info info
info.pass = info.pass.trim();
client.pass = info.pass;
......@@ -3055,6 +3057,22 @@
ygopro.stoc_die(client, '${invalid_password_payload}');
return;
}
if (settings.modules.mycard.enabled && settings.modules.mycard.ban_get && !client.is_local) {
axios.get(settings.modules.mycard.ban_get, {
paramsSerializer: qs.stringify,
params: {
user: client.name
}
}).then(function(banMCRequest) {
if (typeof banMCRequest.data === "object") {
return client.ban_mc = banMCRequest.data;
} else {
return log.warn("ban get bad json", banMCRequest.data);
}
}).catch(function(e) {
return log.warn('ban get error', e.toString());
});
}
check_buffer_indentity = function(buf) {
var checksum, i, m, ref;
checksum = 0;
......@@ -3063,8 +3081,8 @@
}
return (checksum & 0xFF) === 0;
};
create_room_with_action = async function(buffer, decrypted_buffer, match_permit) {
var action, firstByte, len2, m, name, opt0, opt1, opt2, opt3, options, player, ref, ref1, room, room_title, title;
create_room_with_action = async function(buffer, decrypted_buffer) {
var action, e, firstByte, len2, m, matchPermitRes, match_permit, name, opt0, opt1, opt2, opt3, options, player, ref, ref1, room, room_title, title;
if (client.closed) {
return;
}
......@@ -3094,9 +3112,9 @@
options = {
lflist: settings.hostinfo.lflist,
time_limit: settings.hostinfo.time_limit,
rule: (opt1 >> 5) & 0x7, // 0 1 2 3 4
rule: (opt1 >> 5) & 0x7, // 0 1 2 3 4 5
mode: (opt1 >> 3) & 0x3, // 0 1 2
duel_rule: opt0 >> 1, // 1 2 3 4 5
duel_rule: (opt0 >> 1) || 5, // 1 2 3 4 5
no_check_deck: !!((opt1 >> 1) & 1),
no_shuffle_deck: !!(opt1 & 1),
start_lp: opt2,
......@@ -3108,9 +3126,13 @@
}
};
//console.log(options)
if (options.rule === 2) {
options.lflist = -1;
} else if (options.rule !== 3) {
options.lflist = _.findIndex(lflists, function(list) {
return ((options.rule === 1) === list.tcg) && list.date.isBefore();
});
}
room_title = info.pass.slice(8).replace(String.fromCharCode(0xFEFF), ' ');
if (badwordR.level3.test(room_title)) {
log.warn("BAD ROOM NAME LEVEL 3", room_title, client.name, client.ip);
......@@ -3140,10 +3162,30 @@
}
break;
case 4:
if (match_permit && !match_permit.permit) {
if (settings.modules.arena_mode.check_permit) {
try {
matchPermitRes = (await axios.get(settings.modules.arena_mode.check_permit, {
responseType: 'json',
timeout: 3000,
params: {
username: client.name,
password: info.pass,
arena: settings.modules.arena_mode.mode
}
}));
match_permit = matchPermitRes.data;
} catch (error1) {
e = error1;
log.warn(`match permit fail ${e.toString()}`);
}
if (client.closed) {
return;
}
if (match_permit && match_permit.permit === false) {
ygopro.stoc_die(client, '${invalid_password_unauthorized}');
return;
}
}
room = (await ROOM_find_or_create_by_name('M#' + info.pass.slice(8)));
if (room) {
ref1 = room.get_playing_player();
......@@ -3183,43 +3225,7 @@
room.join_player(client);
}
};
_async.auto({
match_permit: function(done) {
if (client.closed) {
done();
return;
}
if (!settings.modules.arena_mode.check_permit) {
done(null, null);
return;
}
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) {
done(null, body);
} else {
log.warn("Match permit request error", error);
done(null, null);
}
});
},
get_user: function(done) {
var decrypted_buffer, i, id, len2, m, ref, secret;
if (client.closed) {
done();
return;
}
decrypted_buffer = null;
if (id = users_cache[client.name]) {
secret = id % 65535 + 1;
decrypted_buffer = Buffer.allocUnsafe(6);
......@@ -3229,28 +3235,36 @@
decrypted_buffer.writeUInt16LE(buffer.readUInt16LE(i) ^ secret, i);
}
if (check_buffer_indentity(decrypted_buffer)) {
done(null, {
original: decrypted_buffer,
decrypted: decrypted_buffer
});
return;
return create_room_with_action(decrypted_buffer, decrypted_buffer);
}
}
//TODO: query database directly, like preload.
request({
baseUrl: settings.modules.mycard.auth_base_url,
url: '/users/' + encodeURIComponent(client.name) + '.json',
qs: {
try {
userUrl = `${settings.modules.mycard.auth_base_url}/users/${encodeURIComponent(client.name)}.json`;
//console.log(userUrl)
userDataRes = (await axios.get(userUrl, {
responseType: 'json',
timeout: 4000,
params: {
api_key: settings.modules.mycard.auth_key,
api_username: client.name,
skip_track_visit: true
},
json: true
}, function(error, response, body) {
var len3, n, ref1;
if (!error && body && body.user) {
users_cache[client.name] = body.user.id;
secret = body.user.id % 65535 + 1;
}
}));
userData = userDataRes.data;
} catch (error1) {
//console.log userData
e = error1;
log.warn("READ USER FAIL", client.name, e.toString());
if (!client.closed) {
ygopro.stoc_die(client, '${load_user_info_fail}');
}
return;
}
if (client.closed) {
return;
}
users_cache[client.name] = userData.user.id;
secret = userData.user.id % 65535 + 1;
decrypted_buffer = Buffer.allocUnsafe(6);
ref1 = [0, 2, 4];
for (n = 0, len3 = ref1.length; n < len3; n++) {
......@@ -3260,31 +3274,11 @@
if (check_buffer_indentity(decrypted_buffer)) {
buffer = decrypted_buffer;
}
} else {
log.warn("READ USER FAIL", client.name, error, body);
done("${load_user_info_fail}");
return;
}
if (!check_buffer_indentity(buffer)) {
done('${invalid_password_checksum}');
return;
}
done(null, {
original: buffer,
decrypted: decrypted_buffer
});
});
}
}, function(err, data) {
if (client.closed) {
ygopro.stoc_die(client, '${invalid_password_checksum}');
return;
}
if (err) {
ygopro.stoc_die(client, err);
return;
}
return create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit);
});
return create_room_with_action(buffer, decrypted_buffer);
} else if (settings.modules.challonge.enabled) {
if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
info.version = settings.version;
......@@ -3316,7 +3310,7 @@
});
}
}, async function(err, datas) {
var create_room_name, found, k, match, ref, ref1, room, user;
var create_room_name, found, k, match, ref2, ref3, room, user;
if (client.closed) {
return;
}
......@@ -3326,9 +3320,9 @@
return;
}
found = false;
ref = datas.participant_data;
for (k in ref) {
user = ref[k];
ref2 = datas.participant_data;
for (k in ref2) {
user = ref2[k];
if (user.participant && user.participant.name && deck_name_match(user.participant.name, client.name)) {
found = user.participant;
break;
......@@ -3340,9 +3334,9 @@
}
client.challonge_info = found;
found = false;
ref1 = datas.match_data;
for (k in ref1) {
match = ref1[k];
ref3 = datas.match_data;
for (k in ref3) {
match = ref3[k];
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;
......@@ -3355,10 +3349,15 @@
//if found.winnerId
// ygopro.stoc_die(client, '${challonge_match_already_finished}')
// return
create_room_name = 'M#' + found.id;
create_room_name = found.id.toString();
if (!settings.modules.challonge.no_match_mode) {
create_room_name = 'M#' + create_room_name;
if (recover_match) {
create_room_name = recover_match[0] + ',' + create_room_name;
}
} else if (recover_match) {
create_room_name = recover_match[0] + '#' + create_room_name;
}
room = (await ROOM_find_or_create_by_name(create_room_name));
if (room) {
room.challonge_info = found;
......@@ -3588,7 +3587,7 @@
return true;
} else {
room.waiting_for_player = client;
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
}
//log.info("#{msg_name}等待#{room.waiting_for_player.name}")
......@@ -3747,6 +3746,7 @@
pos = pos * 2;
}
val = buffer.readInt32LE(2);
if (room.dueling_players[pos]) {
room.dueling_players[pos].lp -= val;
if (room.dueling_players[pos].lp < 0) {
room.dueling_players[pos].lp = 0;
......@@ -3755,6 +3755,7 @@
ygopro.stoc_send_chat_to_room(room, "${lp_low_opponent}", ygopro.constants.COLORS.PINK);
}
}
}
if (msg_name === 'RECOVER' && client.pos === 0) {
pos = buffer.readUInt8(1);
if (!client.is_first) {
......@@ -3764,8 +3765,10 @@
pos = pos * 2;
}
val = buffer.readInt32LE(2);
if (room.dueling_players[pos]) {
room.dueling_players[pos].lp += val;
}
}
if (msg_name === 'LPUPDATE' && client.pos === 0) {
pos = buffer.readUInt8(1);
if (!client.is_first) {
......@@ -3775,8 +3778,10 @@
pos = pos * 2;
}
val = buffer.readInt32LE(2);
if (room.dueling_players[pos]) {
room.dueling_players[pos].lp = val;
}
}
if (msg_name === 'PAY_LPCOST' && client.pos === 0) {
pos = buffer.readUInt8(1);
if (!client.is_first) {
......@@ -3786,6 +3791,7 @@
pos = pos * 2;
}
val = buffer.readInt32LE(2);
if (room.dueling_players[pos]) {
room.dueling_players[pos].lp -= val;
if (room.dueling_players[pos].lp < 0) {
room.dueling_players[pos].lp = 0;
......@@ -3794,6 +3800,7 @@
ygopro.stoc_send_chat_to_room(room, "${lp_low_self}", ygopro.constants.COLORS.PINK);
}
}
}
//track card count
//todo: track card count in tag mode
if (msg_name === 'MOVE' && room.hostinfo.mode !== 2) {
......@@ -4073,9 +4080,11 @@
if (room.waiting_for_player !== room.waiting_for_player2) {
room.waiting_for_player2 = room.waiting_for_player;
room.waiting_for_player_time = settings.modules.arena_mode.ready_time;
if (!room.waiting_for_player_interval) {
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;
......@@ -4144,6 +4153,7 @@
wait_room_start = async function(room, time) {
var j, len, player, ref;
if (room && room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.ready_player_count_without_host >= room.max_player - 1) {
//log.info('wait room start', time)
time -= 1;
if (time) {
if (!(time % 5)) {
......@@ -4435,7 +4445,7 @@
msg = _.trim(info.msg);
cancel = _.startsWith(msg, "/");
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)) {
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
cmd = msg.split(' ');
isVip = (await CLIENT_check_vip(client));
......@@ -4453,7 +4463,7 @@
ygopro.ctos_send(client.server, 'SURRENDER');
} else {
sur_player = CLIENT_get_partner(client);
if (sur_player.closed || sur_player.is_local) {
if (!sur_player || sur_player.closed || sur_player.is_local) {
sur_player = client;
}
if (room.hostinfo.mode === 2 && sur_player !== client) {
......@@ -4468,6 +4478,7 @@
case '/help':
ygopro.stoc_send_chat(client, "${chat_order_main}");
ygopro.stoc_send_chat(client, "${chat_order_help}");
ygopro.stoc_send_chat(client, "${chat_order_refresh}");
if (!settings.modules.mycard.enabled) {
ygopro.stoc_send_chat(client, "${chat_order_roomname}");
}
......@@ -4519,6 +4530,17 @@
ygopro.stoc_send_chat(client, "${room_name} " + room.name, ygopro.constants.COLORS.BABYBLUE);
}
break;
case '/refresh':
if (room.duel_stage === ygopro.constants.DUEL_STAGE.DUELING && 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);
ygopro.stoc_send_chat(client, '${refresh_success}', ygopro.constants.COLORS.BABYBLUE);
} else {
ygopro.stoc_send_chat(client, '${refresh_fail}', ygopro.constants.COLORS.RED);
}
break;
case '/color':
if (settings.modules.chat_color.enabled) {
cip = CLIENT_get_authorize_key(client);
......@@ -4661,7 +4683,7 @@
ygopro.stoc_send_chat(client, "${chat_warn_level0}", ygopro.constants.COLORS.RED);
cancel = true;
}
if (!(room && (room.random_type || room.arena))) {
if (!(room && (room.random_type || room.arena)) && !settings.modules.mycard.enabled) {
if (!cancel && settings.modules.display_watchers && client.is_post_watcher) {
ygopro.stoc_send_chat_to_room(room, `${client.name}: ${msg}`, 9);
return true;
......@@ -4739,7 +4761,7 @@
});
ygopro.ctos_follow('UPDATE_DECK', true, async function(buffer, info, client, server, datas) {
var buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, j, l, len, len1, line, oppo_pos, recover_player_data, recoveredDeck, room, struct, win_pos;
var athleticCheckResult, buff_main, buff_side, card, current_deck, deck, deck_array, deck_main, deck_side, deck_text, deckbuf, decks, found_deck, i, j, l, len, len1, line, oppo_pos, recover_player_data, recoveredDeck, room, struct, win_pos;
if (settings.modules.reconnect.enabled && client.pre_reconnecting) {
if (!CLIENT_is_able_to_reconnect(client) && !CLIENT_is_able_to_kick_reconnect(client)) {
ygopro.stoc_send_chat(client, "${reconnect_failed}", ygopro.constants.COLORS.RED);
......@@ -4815,7 +4837,7 @@
if (client.pos === 0) {
room.waiting_for_player = room.waiting_for_player2;
}
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && room.recovering) {
recover_player_data = _.find(room.recover_duel_log.players, function(player) {
......@@ -4834,8 +4856,27 @@
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED);
return false;
}
} else if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.deck_check) {
} else {
if (room.arena && settings.modules.athletic_check.enabled && settings.modules.athletic_check.banCount) {
athleticCheckResult = (await athleticChecker.checkAthletic({
main: buff_main,
side: buff_side
}));
if (athleticCheckResult.success) {
if (athleticCheckResult.athletic && athleticCheckResult.athletic <= settings.modules.athletic_check.banCount) {
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
ygopro.stoc_send_chat(client, `\${banned_athletic_deck_part1}${settings.modules.athletic_check.banCount}\${banned_athletic_deck_part2}`, ygopro.constants.COLORS.RED);
return false;
}
} else {
log.warn("GET ATHLETIC FAIL", client.name, athleticCheckResult.message);
}
}
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN && settings.modules.tournament_mode.enabled && settings.modules.tournament_mode.deck_check) {
decks = (await fs.promises.readdir(settings.modules.tournament_mode.deck_path));
if (decks.length) {
struct.set("mainc", 1);
......@@ -4878,10 +4919,13 @@
} else {
//log.info("bad deck: " + client.name + " / " + buff_main + " / " + buff_side)
ygopro.stoc_send_chat(client, `\${deck_incorrect_part1} ${found_deck} \${deck_incorrect_part2}`, ygopro.constants.COLORS.RED);
return false;
}
} else {
//log.info("player deck not found: " + client.name)
ygopro.stoc_send_chat(client, `${client.name}\${deck_not_found}`, ygopro.constants.COLORS.RED);
return false;
}
}
}
}
......@@ -4894,7 +4938,7 @@
if (!(room && (room.random_type || room.arena))) {
return;
}
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
});
ygopro.stoc_follow('TIME_LIMIT', true, async function(buffer, info, client, server, datas) {
......@@ -4993,7 +5037,7 @@
if (client.pos === 0) {
room.waiting_for_player = room.waiting_for_player2;
}
room.last_active_time = moment_long_ago_string;
room.refreshLastActiveTime(true);
}
});
......@@ -5008,7 +5052,7 @@
if (!(room.random_type || room.arena)) {
return;
}
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
});
ygopro.stoc_follow('CHAT', true, async function(buffer, info, client, server, datas) {
......@@ -5065,7 +5109,7 @@
} else {
room.waiting_for_player2 = client;
}
room.last_active_time = moment_long_ago_string;
room.refreshLastActiveTime(true);
}
if (room.determine_firstgo) {
ygopro.ctos_send(server, "HAND_RESULT", {
......@@ -5096,7 +5140,7 @@
room.duel_stage = ygopro.constants.DUEL_STAGE.FIRSTGO;
if (room.random_type || room.arena) {
room.waiting_for_player = client;
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
if (room.determine_firstgo) {
ygopro.ctos_send(server, "TP_RESULT", {
......@@ -5165,7 +5209,7 @@
} else {
room.waiting_for_player2 = client;
}
room.last_active_time = moment_now_string;
room.refreshLastActiveTime();
}
});
......
......@@ -113,4 +113,4 @@ translateHandler = (handler) ->
if client
client.system_kicked = true
client.destroy()
return
return '_cancel'
......@@ -185,6 +185,7 @@
client.system_kicked = true;
client.destroy();
}
return '_cancel';
};
}).call(this);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment