Commit bc7b9de6 authored by nanahira's avatar nanahira

Merge branch 'database' into databasen

parents 70ef0ba3 04888860
...@@ -14,6 +14,7 @@ config.user.bak ...@@ -14,6 +14,7 @@ config.user.bak
/windbot /windbot
/decks /decks
/decks_save* /decks_save*
/deck_log
/replays /replays
/node_modules /node_modules
/ssl /ssl
......
...@@ -117,7 +117,7 @@ class Replay ...@@ -117,7 +117,7 @@ class Replay
@header == null ? false : @header.isTag @header == null ? false : @header.isTag
@fromFile: (filePath) -> @fromFile: (filePath) ->
Replay.fromBuffer fs.readFileSync filePath Replay.fromBuffer await fs.promises.readFile filePath
@fromBuffer: (buffer) -> @fromBuffer: (buffer) ->
reader = new ReplayReader buffer reader = new ReplayReader buffer
......
...@@ -175,8 +175,8 @@ ...@@ -175,8 +175,8 @@
}; };
} }
static fromFile(filePath) { static async fromFile(filePath) {
return Replay.fromBuffer(fs.readFileSync(filePath)); return Replay.fromBuffer((await fs.promises.readFile(filePath)));
} }
static fromBuffer(buffer) { static fromBuffer(buffer) {
......
...@@ -11,6 +11,8 @@ const CloudReplayPlayer_1 = require("./entities/CloudReplayPlayer"); ...@@ -11,6 +11,8 @@ const CloudReplayPlayer_1 = require("./entities/CloudReplayPlayer");
const Ban_1 = require("./entities/Ban"); const Ban_1 = require("./entities/Ban");
const RandomDuelBan_1 = require("./entities/RandomDuelBan"); const RandomDuelBan_1 = require("./entities/RandomDuelBan");
const underscore_1 = __importDefault(require("underscore")); const underscore_1 = __importDefault(require("underscore"));
const DuelLog_1 = require("./entities/DuelLog");
const DuelLogPlayer_1 = require("./entities/DuelLogPlayer");
class DataManager { class DataManager {
constructor(config, log) { constructor(config, log) {
this.config = config; this.config = config;
...@@ -183,6 +185,93 @@ class DataManager { ...@@ -183,6 +185,93 @@ class DataManager {
return null; return null;
} }
} }
async getAllDuelLogs() {
const repo = this.db.getRepository(DuelLog_1.DuelLog);
try {
const allDuelLogs = await repo.find({ relations: ["players"] });
return allDuelLogs;
}
catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return [];
}
}
async getDuelLogFromId(id) {
const repo = this.db.getRepository(DuelLog_1.DuelLog);
try {
const duelLog = await repo.findOne(id, { relations: ["players"] });
return duelLog;
}
catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
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 })
.orderBy("duelLog.id", "DESC")
.limit(10)
.leftJoinAndSelect("duelLog.players", "player")
.getMany();
return duelLogs;
}
catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
async getDuelLogJSON(tournamentModeSettings) {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.getViewJSON(tournamentModeSettings));
}
async getAllReplayFilenames() {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.replayFileName);
}
async clearDuelLog() {
//await this.db.transaction(async (mdb) => {
const runner = this.db.createQueryRunner();
try {
await runner.connect();
await runner.startTransaction();
await runner.query("SET FOREIGN_KEY_CHECKS = 0; ");
await runner.clearTable("duel_log_player");
await runner.clearTable("duel_log");
await runner.query("SET FOREIGN_KEY_CHECKS = 1; ");
await runner.commitTransaction();
}
catch (e) {
await runner.rollbackTransaction();
this.log.warn(`Failed to clear duel logs: ${e.toString()}`);
}
//});
}
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.roomId = roomId;
duelLog.cloudReplayId = cloudReplayId;
duelLog.replayFileName = replayFilename;
duelLog.roomMode = roomMode;
duelLog.duelCount = duelCount;
const players = playerInfos.map(p => DuelLogPlayer_1.DuelLogPlayer.fromDuelLogPlayerInfo(p));
await this.db.transaction(async (mdb) => {
try {
const savedDuelLog = await mdb.save(duelLog);
for (let player of players) {
player.duelLog = savedDuelLog;
}
await mdb.save(players);
}
catch (e) {
this.log.warn(`Failed to save duel log ${name}: ${e.toString()}`);
}
});
}
} }
exports.DataManager = DataManager; exports.DataManager = DataManager;
//# sourceMappingURL=DataManager.js.map //# sourceMappingURL=DataManager.js.map
\ No newline at end of file
import moment from "moment"; import moment from "moment";
import { Moment } from "moment"; import { Moment } from "moment";
import bunyan from "bunyan"; import bunyan from "bunyan";
import { Connection, ConnectionOptions, createConnection, Transaction } from "typeorm"; import {Connection, ConnectionOptions, createConnection, Transaction} from "typeorm";
import { CloudReplay } from "./entities/CloudReplay"; import { CloudReplay } from "./entities/CloudReplay";
import { CloudReplayPlayer } from "./entities/CloudReplayPlayer"; import { CloudReplayPlayer } from "./entities/CloudReplayPlayer";
import { Ban } from "./entities/Ban"; import { Ban } from "./entities/Ban";
import {RandomDuelBan} from "./entities/RandomDuelBan"; import {RandomDuelBan} from "./entities/RandomDuelBan";
import _ from "underscore"; import _ from "underscore";
import {DuelLog} from "./entities/DuelLog";
import {Deck} from "./DeckEncoder";
import {DuelLogPlayer} from "./entities/DuelLogPlayer";
interface BasePlayerInfo {
export interface CloudReplayPlayerInfo {
name: string; name: string;
key: string;
pos: number pos: number
} }
export interface CloudReplayPlayerInfo extends BasePlayerInfo {
key: string;
}
export interface DuelLogPlayerInfo extends BasePlayerInfo {
realName: string;
startDeckBuffer: Buffer;
deck: Deck;
isFirst: boolean;
winner: boolean;
ip: string;
score: number;
lp: number;
cardCount: number;
}
export class DataManager { export class DataManager {
config: ConnectionOptions; config: ConnectionOptions;
ready: boolean; ready: boolean;
...@@ -158,7 +176,7 @@ export class DataManager { ...@@ -158,7 +176,7 @@ export class DataManager {
} }
} }
async randomDuelBanPlayer(ip: string, reason: string, countadd?: number){ async randomDuelBanPlayer(ip: string, reason: string, countadd?: number) {
const count = countadd || 1; const count = countadd || 1;
const repo = this.db.getRepository(RandomDuelBan); const repo = this.db.getRepository(RandomDuelBan);
try { try {
...@@ -191,4 +209,96 @@ export class DataManager { ...@@ -191,4 +209,96 @@ export class DataManager {
} }
} }
async getAllDuelLogs() {
const repo = this.db.getRepository(DuelLog);
try {
const allDuelLogs = await repo.find({relations: ["players"]});
return allDuelLogs;
} catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return [];
}
}
async getDuelLogFromId(id: number) {
const repo = this.db.getRepository(DuelLog);
try {
const duelLog = await repo.findOne(id, {relations: ["players"]});
return duelLog;
} catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
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 })
.orderBy("duelLog.id", "DESC")
.limit(10)
.leftJoinAndSelect("duelLog.players", "player")
.getMany();
return duelLogs;
} catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
async getDuelLogJSON(tournamentModeSettings: any) {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.getViewJSON(tournamentModeSettings));
}
async getAllReplayFilenames() {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.replayFileName);
}
async clearDuelLog() {
//await this.db.transaction(async (mdb) => {
const runner = this.db.createQueryRunner();
try {
await runner.connect();
await runner.startTransaction();
await runner.query("SET FOREIGN_KEY_CHECKS = 0; ");
await runner.clearTable("duel_log_player");
await runner.clearTable("duel_log");
await runner.query("SET FOREIGN_KEY_CHECKS = 1; ");
await runner.commitTransaction();
} catch (e) {
await runner.rollbackTransaction();
this.log.warn(`Failed to clear duel logs: ${e.toString()}`);
}
//});
}
async saveDuelLog(name: string, roomId: number, cloudReplayId: number, replayFilename: string, roomMode: number, duelCount: number, playerInfos: DuelLogPlayerInfo[]) {
const duelLog = new DuelLog();
duelLog.name = name;
duelLog.time = moment().toDate();
duelLog.roomId = roomId;
duelLog.cloudReplayId = cloudReplayId;
duelLog.replayFileName = replayFilename;
duelLog.roomMode = roomMode;
duelLog.duelCount = duelCount;
const players = playerInfos.map(p => DuelLogPlayer.fromDuelLogPlayerInfo(p));
await this.db.transaction(async (mdb) => {
try {
const savedDuelLog = await mdb.save(duelLog);
for (let player of players) {
player.duelLog = savedDuelLog;
}
await mdb.save(players);
} catch (e) {
this.log.warn(`Failed to save duel log ${name}: ${e.toString()}`);
}
});
}
} }
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeDeck = exports.encodeDeck = void 0;
const assert_1 = __importDefault(require("assert"));
function encodeDeck(deck) {
let pointer = 0;
const bufferSize = (2 + deck.main.length + deck.side.length) * 4;
const buffer = Buffer.allocUnsafe(bufferSize);
buffer.writeInt32LE(deck.main.length, pointer);
pointer += 4;
buffer.writeInt32LE(deck.side.length, pointer);
pointer += 4;
for (let cardCode of deck.main.concat(deck.side)) {
buffer.writeInt32LE(cardCode, pointer);
pointer += 4;
}
assert_1.default(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
return buffer;
}
exports.encodeDeck = encodeDeck;
function decodeDeck(buffer) {
let pointer = 0;
const mainLength = buffer.readInt32LE(pointer);
pointer += 4;
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}`);
const main = [];
const side = [];
for (let i = 0; i < mainLength; ++i) {
main.push(buffer.readInt32LE(pointer));
pointer += 4;
}
for (let i = 0; i < sideLength; ++i) {
side.push(buffer.readInt32LE(pointer));
pointer += 4;
}
return { main, side };
}
exports.decodeDeck = decodeDeck;
//# sourceMappingURL=DeckEncoder.js.map
\ No newline at end of file
import assert from "assert";
export interface Deck {
main: number[];
side: number[];
}
export function encodeDeck(deck: Deck) {
let pointer = 0;
const bufferSize = (2 + deck.main.length + deck.side.length) * 4;
const buffer = Buffer.allocUnsafe(bufferSize);
buffer.writeInt32LE(deck.main.length, pointer);
pointer += 4;
buffer.writeInt32LE(deck.side.length, pointer);
pointer += 4;
for(let cardCode of deck.main.concat(deck.side)) {
buffer.writeInt32LE(cardCode, pointer);
pointer += 4;
}
assert(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
return buffer;
}
export function decodeDeck(buffer: Buffer): Deck {
let pointer = 0;
const mainLength = buffer.readInt32LE(pointer);
pointer += 4;
const sideLength = buffer.readInt32LE(pointer);
pointer += 4;
const correctBufferLength = (2 + mainLength + sideLength) * 4;
assert(buffer.length >= (2 + mainLength + sideLength) * 4, `Invalid buffer size. Expected: ${correctBufferLength}. Got: ${buffer.length}`);
const main: number[] = [];
const side: number[] = [];
for(let i = 0; i < mainLength; ++i) {
main.push(buffer.readInt32LE(pointer));
pointer += 4;
}
for(let i = 0; i < sideLength; ++i) {
side.push(buffer.readInt32LE(pointer));
pointer += 4;
}
return {main, side};
}
\ No newline at end of file
...@@ -11,27 +11,24 @@ var __metadata = (this && this.__metadata) || function (k, v) { ...@@ -11,27 +11,24 @@ var __metadata = (this && this.__metadata) || function (k, v) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.Ban = void 0; exports.Ban = void 0;
const typeorm_1 = require("typeorm"); const typeorm_1 = require("typeorm");
let Ban = /** @class */ (() => { let Ban = class Ban {
let Ban = class Ban { };
}; __decorate([
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }), typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number) __metadata("design:type", Number)
], Ban.prototype, "id", void 0); ], Ban.prototype, "id", void 0);
__decorate([ __decorate([
typeorm_1.Index(), typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 64, nullable: true }), typeorm_1.Column({ type: "varchar", length: 64, nullable: true }),
__metadata("design:type", String) __metadata("design:type", String)
], Ban.prototype, "ip", void 0); ], Ban.prototype, "ip", void 0);
__decorate([ __decorate([
typeorm_1.Index(), typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20, nullable: true }), typeorm_1.Column({ type: "varchar", length: 20, nullable: true }),
__metadata("design:type", String) __metadata("design:type", String)
], Ban.prototype, "name", void 0); ], Ban.prototype, "name", void 0);
Ban = __decorate([ Ban = __decorate([
typeorm_1.Entity() typeorm_1.Entity()
], Ban); ], Ban);
return Ban;
})();
exports.Ban = Ban; exports.Ban = Ban;
//# sourceMappingURL=Ban.js.map //# sourceMappingURL=Ban.js.map
\ No newline at end of file
import { Column, Entity, Index, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm"; import {Column, Entity, Index, PrimaryGeneratedColumn} from "typeorm";
@Entity() @Entity()
export class Ban { export class Ban {
......
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasePlayer = void 0;
const typeorm_1 = require("typeorm");
class BasePlayer {
}
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], BasePlayer.prototype, "id", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], BasePlayer.prototype, "name", void 0);
__decorate([
typeorm_1.Column({ type: "tinyint" }),
__metadata("design:type", Number)
], BasePlayer.prototype, "pos", void 0);
exports.BasePlayer = BasePlayer;
//# sourceMappingURL=BasePlayer.js.map
\ No newline at end of file
import {Column, PrimaryGeneratedColumn} from "typeorm";
export abstract class BasePlayer {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
id: number;
@Column({ type: "varchar", length: 20 })
name: string;
@Column({ type: "tinyint" })
pos: number;
}
\ No newline at end of file
...@@ -17,8 +17,7 @@ const typeorm_1 = require("typeorm"); ...@@ -17,8 +17,7 @@ const typeorm_1 = require("typeorm");
const CloudReplayPlayer_1 = require("./CloudReplayPlayer"); const CloudReplayPlayer_1 = require("./CloudReplayPlayer");
const underscore_1 = __importDefault(require("underscore")); const underscore_1 = __importDefault(require("underscore"));
const moment_1 = __importDefault(require("moment")); const moment_1 = __importDefault(require("moment"));
let CloudReplay = /** @class */ (() => { let CloudReplay = class CloudReplay {
let CloudReplay = class CloudReplay {
fromBuffer(buffer) { fromBuffer(buffer) {
this.data = buffer.toString("base64"); this.data = buffer.toString("base64");
} }
...@@ -36,27 +35,25 @@ let CloudReplay = /** @class */ (() => { ...@@ -36,27 +35,25 @@ let CloudReplay = /** @class */ (() => {
getDisplayString() { getDisplayString() {
return `R#${this.id} ${this.getPlayerNamesString()} ${this.getDateString()}`; return `R#${this.id} ${this.getPlayerNamesString()} ${this.getDateString()}`;
} }
}; };
__decorate([ __decorate([
typeorm_1.PrimaryColumn({ unsigned: true, type: "bigint" }), typeorm_1.PrimaryColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number) __metadata("design:type", Number)
], CloudReplay.prototype, "id", void 0); ], CloudReplay.prototype, "id", void 0);
__decorate([ __decorate([
typeorm_1.Column({ type: "text" }), typeorm_1.Column({ type: "text" }),
__metadata("design:type", String) __metadata("design:type", String)
], CloudReplay.prototype, "data", void 0); ], CloudReplay.prototype, "data", void 0);
__decorate([ __decorate([
typeorm_1.Column({ type: "datetime" }), typeorm_1.Column({ type: "datetime" }),
__metadata("design:type", Date) __metadata("design:type", Date)
], CloudReplay.prototype, "date", void 0); ], CloudReplay.prototype, "date", void 0);
__decorate([ __decorate([
typeorm_1.OneToMany(() => CloudReplayPlayer_1.CloudReplayPlayer, player => player.cloudReplay), typeorm_1.OneToMany(() => CloudReplayPlayer_1.CloudReplayPlayer, player => player.cloudReplay),
__metadata("design:type", Array) __metadata("design:type", Array)
], CloudReplay.prototype, "players", void 0); ], CloudReplay.prototype, "players", void 0);
CloudReplay = __decorate([ CloudReplay = __decorate([
typeorm_1.Entity() typeorm_1.Entity()
], CloudReplay); ], CloudReplay);
return CloudReplay;
})();
exports.CloudReplay = CloudReplay; exports.CloudReplay = CloudReplay;
//# sourceMappingURL=CloudReplay.js.map //# sourceMappingURL=CloudReplay.js.map
\ No newline at end of file
import { Column, Entity, OneToMany, PrimaryColumn } from "typeorm"; import {Column, Entity, OneToMany, PrimaryColumn} from "typeorm";
import { CloudReplayPlayer } from "./CloudReplayPlayer"; import {CloudReplayPlayer} from "./CloudReplayPlayer";
import _ from "underscore"; import _ from "underscore";
import moment from "moment"; import moment from "moment";
......
...@@ -8,13 +8,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key, ...@@ -8,13 +8,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
var __metadata = (this && this.__metadata) || function (k, v) { var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}; };
var CloudReplayPlayer_1;
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.CloudReplayPlayer = void 0; exports.CloudReplayPlayer = void 0;
const typeorm_1 = require("typeorm"); const typeorm_1 = require("typeorm");
const CloudReplay_1 = require("./CloudReplay"); const CloudReplay_1 = require("./CloudReplay");
let CloudReplayPlayer = /** @class */ (() => { const BasePlayer_1 = require("./BasePlayer");
var CloudReplayPlayer_1; let CloudReplayPlayer = CloudReplayPlayer_1 = class CloudReplayPlayer extends BasePlayer_1.BasePlayer {
let CloudReplayPlayer = CloudReplayPlayer_1 = class CloudReplayPlayer {
static fromPlayerInfo(info) { static fromPlayerInfo(info) {
const p = new CloudReplayPlayer_1(); const p = new CloudReplayPlayer_1();
p.key = info.key; p.key = info.key;
...@@ -22,32 +22,18 @@ let CloudReplayPlayer = /** @class */ (() => { ...@@ -22,32 +22,18 @@ let CloudReplayPlayer = /** @class */ (() => {
p.pos = info.pos; p.pos = info.pos;
return p; return p;
} }
}; };
__decorate([ __decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], CloudReplayPlayer.prototype, "id", void 0);
__decorate([
typeorm_1.Index(), typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 40 }), typeorm_1.Column({ type: "varchar", length: 40 }),
__metadata("design:type", String) __metadata("design:type", String)
], CloudReplayPlayer.prototype, "key", void 0); ], CloudReplayPlayer.prototype, "key", void 0);
__decorate([ __decorate([
typeorm_1.Column({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], CloudReplayPlayer.prototype, "name", void 0);
__decorate([
typeorm_1.Column({ type: "tinyint" }),
__metadata("design:type", Number)
], CloudReplayPlayer.prototype, "pos", void 0);
__decorate([
typeorm_1.ManyToOne(() => CloudReplay_1.CloudReplay, replay => replay.players), typeorm_1.ManyToOne(() => CloudReplay_1.CloudReplay, replay => replay.players),
__metadata("design:type", CloudReplay_1.CloudReplay) __metadata("design:type", CloudReplay_1.CloudReplay)
], CloudReplayPlayer.prototype, "cloudReplay", void 0); ], CloudReplayPlayer.prototype, "cloudReplay", void 0);
CloudReplayPlayer = CloudReplayPlayer_1 = __decorate([ CloudReplayPlayer = CloudReplayPlayer_1 = __decorate([
typeorm_1.Entity() typeorm_1.Entity()
], CloudReplayPlayer); ], CloudReplayPlayer);
return CloudReplayPlayer;
})();
exports.CloudReplayPlayer = CloudReplayPlayer; exports.CloudReplayPlayer = CloudReplayPlayer;
//# sourceMappingURL=CloudReplayPlayer.js.map //# sourceMappingURL=CloudReplayPlayer.js.map
\ No newline at end of file
import { Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; import {Column, Entity, Index, ManyToOne} from "typeorm";
import { CloudReplayPlayerInfo } from "../DataManager"; import {CloudReplayPlayerInfo} from "../DataManager";
import { CloudReplay } from "./CloudReplay"; import {CloudReplay} from "./CloudReplay";
import {BasePlayer} from "./BasePlayer";
@Entity() @Entity()
export class CloudReplayPlayer { export class CloudReplayPlayer extends BasePlayer {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
id: number;
@Index() @Index()
@Column({ type: "varchar", length: 40 }) @Column({ type: "varchar", length: 40 })
key: string; key: string;
@Column({ type: "varchar", length: 20 })
name: string;
@Column({ type: "tinyint" })
pos: number;
@ManyToOne(() => CloudReplay, replay => replay.players) @ManyToOne(() => CloudReplay, replay => replay.players)
cloudReplay: CloudReplay; cloudReplay: CloudReplay;
......
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DuelLog = void 0;
const typeorm_1 = require("typeorm");
const DuelLogPlayer_1 = require("./DuelLogPlayer");
const moment_1 = __importDefault(require("moment"));
const underscore_1 = __importDefault(require("underscore"));
let DuelLog = class DuelLog {
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")}`;
}
getViewJSON(tournamentModeSettings) {
const data = {
id: this.id,
time: 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,
replay_filename: this.replayFileName,
roommode: this.roomMode,
players: this.players.map(player => {
return {
pos: player.pos,
is_first: player.isFirst === 1,
name: player.name + (tournamentModeSettings.show_ip ? " (IP: " + player.ip.slice(7) + ")" : "") + (tournamentModeSettings.show_info && !(this.roomMode === 2 && player.pos % 2 > 0) ? " (Score:" + player.score + " LP:" + (player.lp != null ? player.lp : "???") + (this.roomMode !== 2 ? " Cards:" + (player.cardCount != null ? player.cardCount : "???") : "") + ")" : ""),
winner: player.winner === 1
};
})
};
return data;
}
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], DuelLog.prototype, "id", void 0);
__decorate([
typeorm_1.Column("datetime"),
__metadata("design:type", Date)
], DuelLog.prototype, "time", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], DuelLog.prototype, "name", void 0);
__decorate([
typeorm_1.Column("int"),
__metadata("design:type", Number)
], DuelLog.prototype, "roomId", void 0);
__decorate([
typeorm_1.Column("bigint"),
__metadata("design:type", Number)
], DuelLog.prototype, "cloudReplayId", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 256 }),
__metadata("design:type", String)
], DuelLog.prototype, "replayFileName", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLog.prototype, "roomMode", void 0);
__decorate([
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),
__metadata("design:type", Array)
], DuelLog.prototype, "players", void 0);
DuelLog = __decorate([
typeorm_1.Entity()
], DuelLog);
exports.DuelLog = DuelLog;
//# sourceMappingURL=DuelLog.js.map
\ No newline at end of file
import {Column, Entity, Index, OneToMany, PrimaryGeneratedColumn} from "typeorm";
import {DuelLogPlayer} from "./DuelLogPlayer";
import moment from "moment";
import _ from "underscore";
import {DuelLogPlayerInfo} from "../DataManager";
@Entity()
export class DuelLog {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
id: number;
@Column("datetime")
time: Date;
@Index()
@Column({type: "varchar", length: 20})
name: string;
@Column("int")
roomId: number;
@Column("bigint")
cloudReplayId: number; // not very needed to become a relation
@Column({type: "varchar", length: 256})
replayFileName: string;
@Column("tinyint", {unsigned: true})
roomMode: number;
@Column("tinyint", {unsigned: true})
duelCount: number;
@OneToMany(() => DuelLogPlayer, player => player.duelLog)
players: DuelLogPlayer[];
getViewString() {
const viewPlayers = _.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(this.time).format("YYYY-MM-DD HH-mm-ss")}`;
}
getViewJSON(tournamentModeSettings: any) {
const data = {
id: this.id,
time: moment(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,
replay_filename: this.replayFileName,
roommode: this.roomMode,
players: this.players.map(player => {
return {
pos: player.pos,
is_first: player.isFirst === 1,
name: player.name + (tournamentModeSettings.show_ip ? " (IP: " + player.ip.slice(7) + ")" : "") + (tournamentModeSettings.show_info && !(this.roomMode === 2 && player.pos % 2 > 0) ? " (Score:" + player.score + " LP:" + (player.lp != null ? player.lp : "???") + (this.roomMode !== 2 ? " Cards:" + (player.cardCount != null ? player.cardCount : "???") : "") + ")" : ""),
winner: player.winner === 1
}
})
}
return data;
}
}
\ No newline at end of file
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var DuelLogPlayer_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DuelLogPlayer = void 0;
const typeorm_1 = require("typeorm");
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 {
setStartDeck(deck) {
if (deck === null) {
this.startDeckBuffer = null;
return;
}
this.startDeckBuffer = DeckEncoder_1.encodeDeck(deck).toString("base64");
}
getStartDeck() {
return 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");
}
getCurrentDeck() {
return DeckEncoder_1.decodeDeck(Buffer.from(this.currentDeckBuffer, "base64"));
}
static fromDuelLogPlayerInfo(info) {
const p = new DuelLogPlayer_1();
p.name = info.name;
p.pos = info.pos;
p.realName = info.realName;
p.lp = info.lp;
p.ip = info.ip;
p.score = info.score;
p.cardCount = info.cardCount;
p.isFirst = info.isFirst ? 1 : 0;
p.winner = info.winner ? 1 : 0;
p.startDeckBuffer = info.startDeckBuffer.toString("base64");
p.setCurrentDeck(info.deck);
return p;
}
};
__decorate([
typeorm_1.Index(),
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 }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "ip", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "isFirst", void 0);
__decorate([
typeorm_1.Column("tinyint"),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "score", void 0);
__decorate([
typeorm_1.Column("int", { nullable: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "lp", void 0);
__decorate([
typeorm_1.Column("smallint", { nullable: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "cardCount", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "startDeckBuffer", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "currentDeckBuffer", void 0);
__decorate([
typeorm_1.Column("tinyint"),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "winner", void 0);
__decorate([
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()
], DuelLogPlayer);
exports.DuelLogPlayer = DuelLogPlayer;
//# sourceMappingURL=DuelLogPlayer.js.map
\ No newline at end of file
import {Column, Entity, Index, ManyToOne} from "typeorm";
import {BasePlayer} from "./BasePlayer";
import {DuelLog} from "./DuelLog";
import {Deck} from "../DeckEncoder";
import {decodeDeck, encodeDeck} from "../DeckEncoder";
import {DuelLogPlayerInfo} from "../DataManager";
@Entity()
export class DuelLogPlayer extends BasePlayer {
@Index()
@Column({ type: "varchar", length: 20 })
realName: string;
@Column({ type: "varchar", length: 64, nullable: true })
ip: string;
@Column("tinyint", {unsigned: true})
isFirst: number;
@Column("tinyint")
score: number;
@Column("int", {nullable: true})
lp: number;
@Column("smallint", {nullable: true})
cardCount: number;
@Column("text", {nullable: true})
startDeckBuffer: string;
@Column("text", {nullable: true})
currentDeckBuffer: string;
@Column("tinyint")
winner: number;
setStartDeck(deck: Deck) {
if(deck === null) {
this.startDeckBuffer = null;
return;
}
this.startDeckBuffer = encodeDeck(deck).toString("base64");
}
getStartDeck() {
return decodeDeck(Buffer.from(this.startDeckBuffer, "base64"));
}
setCurrentDeck(deck: Deck) {
if(deck === null) {
this.currentDeckBuffer = null;
return;
}
this.currentDeckBuffer = encodeDeck(deck).toString("base64");
}
getCurrentDeck() {
return decodeDeck(Buffer.from(this.currentDeckBuffer, "base64"));
}
@ManyToOne(() => DuelLog, duelLog => duelLog.players)
duelLog: DuelLog;
static fromDuelLogPlayerInfo(info: DuelLogPlayerInfo) {
const p = new DuelLogPlayer();
p.name = info.name;
p.pos = info.pos;
p.realName = info.realName;
p.lp = info.lp;
p.ip = info.ip;
p.score = info.score;
p.cardCount = info.cardCount;
p.isFirst = info.isFirst ? 1 : 0;
p.winner = info.winner ? 1 : 0;
p.startDeckBuffer = info.startDeckBuffer.toString("base64");
p.setCurrentDeck(info.deck);
return p;
}
}
...@@ -11,39 +11,36 @@ var __metadata = (this && this.__metadata) || function (k, v) { ...@@ -11,39 +11,36 @@ var __metadata = (this && this.__metadata) || function (k, v) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.RandomDuelBan = void 0; exports.RandomDuelBan = void 0;
const typeorm_1 = require("typeorm"); const typeorm_1 = require("typeorm");
let RandomDuelBan = /** @class */ (() => { let RandomDuelBan = class RandomDuelBan {
let RandomDuelBan = class RandomDuelBan {
setNeedTip(need) { setNeedTip(need) {
this.needTip = need ? 1 : 0; this.needTip = need ? 1 : 0;
} }
getNeedTip() { getNeedTip() {
return this.needTip > 0 ? true : false; return this.needTip > 0 ? true : false;
} }
}; };
__decorate([ __decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 64 }), typeorm_1.PrimaryColumn({ type: "varchar", length: 64 }),
__metadata("design:type", String) __metadata("design:type", String)
], RandomDuelBan.prototype, "ip", void 0); ], RandomDuelBan.prototype, "ip", void 0);
__decorate([ __decorate([
typeorm_1.Column("datetime"), typeorm_1.Column("datetime"),
__metadata("design:type", Date) __metadata("design:type", Date)
], RandomDuelBan.prototype, "time", void 0); ], RandomDuelBan.prototype, "time", void 0);
__decorate([ __decorate([
typeorm_1.Column("smallint"), typeorm_1.Column("smallint"),
__metadata("design:type", Number) __metadata("design:type", Number)
], RandomDuelBan.prototype, "count", void 0); ], RandomDuelBan.prototype, "count", void 0);
__decorate([ __decorate([
typeorm_1.Column({ type: "simple-array" }), typeorm_1.Column({ type: "simple-array" }),
__metadata("design:type", Array) __metadata("design:type", Array)
], RandomDuelBan.prototype, "reasons", void 0); ], RandomDuelBan.prototype, "reasons", void 0);
__decorate([ __decorate([
typeorm_1.Column({ type: "tinyint", unsigned: true }), typeorm_1.Column({ type: "tinyint", unsigned: true }),
__metadata("design:type", Number) __metadata("design:type", Number)
], RandomDuelBan.prototype, "needTip", void 0); ], RandomDuelBan.prototype, "needTip", void 0);
RandomDuelBan = __decorate([ RandomDuelBan = __decorate([
typeorm_1.Entity() typeorm_1.Entity()
], RandomDuelBan); ], RandomDuelBan);
return RandomDuelBan;
})();
exports.RandomDuelBan = RandomDuelBan; exports.RandomDuelBan = RandomDuelBan;
//# sourceMappingURL=RandomDuelBan.js.map //# sourceMappingURL=RandomDuelBan.js.map
\ No newline at end of file
...@@ -426,6 +426,11 @@ ...@@ -426,6 +426,11 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
}, },
"coffeescript": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz",
"integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ=="
},
"color-convert": { "color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
...@@ -1988,6 +1993,11 @@ ...@@ -1988,6 +1993,11 @@
} }
} }
}, },
"typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ=="
},
"ultron": { "ultron": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
"axios": "^0.19.2", "axios": "^0.19.2",
"bunyan": "^1.8.14", "bunyan": "^1.8.14",
"challonge": "latest", "challonge": "latest",
"coffeescript": "^2.5.1",
"deepmerge": "latest", "deepmerge": "latest",
"formidable": "latest", "formidable": "latest",
"geoip-country-lite": "latest", "geoip-country-lite": "latest",
...@@ -32,12 +33,14 @@ ...@@ -32,12 +33,14 @@
"request": "latest", "request": "latest",
"sqlite3": "latest", "sqlite3": "latest",
"typeorm": "^0.2.29", "typeorm": "^0.2.29",
"typescript": "^4.0.5",
"underscore": "latest", "underscore": "latest",
"underscore.string": "latest", "underscore.string": "latest",
"ws": "^1.1.1" "ws": "^1.1.1"
}, },
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"build": "coffee -c *.coffee && tsc",
"start": "node ygopro-server.js", "start": "node ygopro-server.js",
"tournament": "node ygopro-tournament.js", "tournament": "node ygopro-tournament.js",
"pre": "node ygopro-pre.js", "pre": "node ygopro-pre.js",
......
...@@ -59,7 +59,7 @@ add_log = (message) -> ...@@ -59,7 +59,7 @@ add_log = (message) ->
text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n" text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n"
res = false res = false
try try
await util.promisify(fs.appendFile)("./logs/"+mt.format('YYYY-MM-DD')+".log", text) await fs.promises.appendFile("./logs/"+mt.format('YYYY-MM-DD')+".log", text)
res = true res = true
catch catch
res = false res = false
...@@ -69,7 +69,7 @@ add_log = (message) -> ...@@ -69,7 +69,7 @@ add_log = (message) ->
default_data = loadJSON('./data/default_data.json') default_data = loadJSON('./data/default_data.json')
setting_save = (settings) -> setting_save = (settings) ->
try try
await util.promisify(fs.writeFile)(settings.file, JSON.stringify(settings, null, 2)) await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2))
catch e catch e
add_log("save fail"); add_log("save fail");
return return
......
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n"; text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n";
res = false; res = false;
try { try {
await util.promisify(fs.appendFile)("./logs/" + mt.format('YYYY-MM-DD') + ".log", text); await fs.promises.appendFile("./logs/" + mt.format('YYYY-MM-DD') + ".log", text);
res = true; res = true;
} catch (error) { } catch (error) {
res = false; res = false;
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
setting_save = async function(settings) { setting_save = async function(settings) {
var e; var e;
try { try {
await util.promisify(fs.writeFile)(settings.file, JSON.stringify(settings, null, 2)); await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2));
} catch (error) { } catch (error) {
e = error; e = error;
add_log("save fail"); add_log("save fail");
......
...@@ -21,6 +21,7 @@ request = require 'request' ...@@ -21,6 +21,7 @@ request = require 'request'
axios = require 'axios' axios = require 'axios'
qs = require "querystring" qs = require "querystring"
zlib = require 'zlib' zlib = require 'zlib'
axios = require 'axios'
bunyan = require 'bunyan' bunyan = require 'bunyan'
log = global.log = bunyan.createLogger name: "mycard" log = global.log = bunyan.createLogger name: "mycard"
...@@ -84,79 +85,33 @@ merge = require 'deepmerge' ...@@ -84,79 +85,33 @@ merge = require 'deepmerge'
loadJSON = require('load-json-file').sync loadJSON = require('load-json-file').sync
loadJSONAsync = require('load-json-file')
util = require("util") util = require("util")
Q = require("q") Q = require("q")
#heapdump = require 'heapdump' #heapdump = require 'heapdump'
# 配置 checkFileExists = (path) =>
# 导入旧配置 try
if not fs.existsSync('./config') await fs.promises.access(path)
fs.mkdirSync('./config') return true
try catch e
oldconfig=loadJSON('./config.user.json') return false
if oldconfig.tips
oldtips = {}
oldtips.file = './config/tips.json'
oldtips.tips = oldconfig.tips
oldtips.tips_zh = []
fs.writeFileSync(oldtips.file, JSON.stringify(oldtips, null, 2))
delete oldconfig.tips
if oldconfig.words
oldwords = {}
oldwords.file = './config/words.json'
oldwords.words = oldconfig.words
fs.writeFileSync(oldwords.file, JSON.stringify(oldwords, null, 2))
delete oldconfig.words
if oldconfig.dialogues
olddialogues = {}
olddialogues.file = './config/dialogues.json'
olddialogues.dialogues = oldconfig.dialogues
olddialogues.dialogues_custom = {}
fs.writeFileSync(olddialogues.file, JSON.stringify(olddialogues, null, 2))
delete oldconfig.dialogues
if oldconfig.modules
if oldconfig.modules.tournament_mode and oldconfig.modules.tournament_mode.duel_log
oldduellog = {}
oldduellog.file = './config/duel_log.json'
oldduellog.duel_log = oldconfig.modules.tournament_mode.duel_log
fs.writeFileSync(oldduellog.file, JSON.stringify(oldduellog, null, 2))
delete oldconfig.oldduellog
oldbadwords={}
if oldconfig.ban
if oldconfig.ban.badword_level0
oldbadwords.level0 = oldconfig.ban.badword_level0
if oldconfig.ban.badword_level1
oldbadwords.level1 = oldconfig.ban.badword_level1
if oldconfig.ban.badword_level2
oldbadwords.level2 = oldconfig.ban.badword_level2
if oldconfig.ban.badword_level3
oldbadwords.level3 = oldconfig.ban.badword_level3
if not _.isEmpty(oldbadwords)
oldbadwords.file = './config/badwords.json'
fs.writeFileSync(oldbadwords.file, JSON.stringify(oldbadwords, null, 2))
delete oldconfig.ban.badword_level0
delete oldconfig.ban.badword_level1
delete oldconfig.ban.badword_level2
delete oldconfig.ban.badword_level3
if not _.isEmpty(oldconfig)
# log.info oldconfig
fs.writeFileSync('./config/config.json', JSON.stringify(oldconfig, null, 2))
log.info 'imported old config from config.user.json'
fs.renameSync('./config.user.json', './config.user.bak')
catch e
log.info e unless e.code == 'ENOENT'
setting_save = global.setting_save = (settings, callback) -> createDirectoryIfNotExists = (path) =>
if !callback if !await checkFileExists(path)
callback = (err) -> await fs.promises.mkdir(path, {recursive: true})
if(err)
log.warn("setting save fail", err.toString()) setting_save = global.setting_save = (settings) ->
fs.writeFile(settings.file, JSON.stringify(settings, null, 2), callback) try
await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2))
catch e
log.warn("setting save fail", e.toString())
return return
setting_change = global.setting_change = (settings, path, val, callback) -> setting_change = global.setting_change = (settings, path, val) ->
# path should be like "modules:welcome" # path should be like "modules:welcome"
log.info("setting changed", path, val) if _.isString(val) log.info("setting changed", path, val) if _.isString(val)
path=path.split(':') path=path.split(':')
...@@ -169,7 +124,7 @@ setting_change = global.setting_change = (settings, path, val, callback) -> ...@@ -169,7 +124,7 @@ setting_change = global.setting_change = (settings, path, val, callback) ->
target=target[key] target=target[key]
key = path.shift() key = path.shift()
target[key] = val target[key] = val
setting_save(settings, callback) await setting_save(settings)
return return
VIP_generate_cdkeys = global.VIP_generate_cdkeys = (key_type, count) -> VIP_generate_cdkeys = global.VIP_generate_cdkeys = (key_type, count) ->
...@@ -251,30 +206,126 @@ concat_name = global.concat_name = (name, num) -> ...@@ -251,30 +206,126 @@ concat_name = global.concat_name = (name, num) ->
count++ count++
return res return res
# 读取配置 importOldConfig = () ->
default_config = loadJSON('./data/default_config.json') try
if fs.existsSync('./config/config.json') oldconfig=await loadJSONAsync('./config.user.json')
if oldconfig.tips
oldtips = {}
oldtips.file = './config/tips.json'
oldtips.tips = oldconfig.tips
await fs.promises.writeFile(oldtips.file, JSON.stringify(oldtips, null, 2))
delete oldconfig.tips
if oldconfig.dialogues
olddialogues = {}
olddialogues.file = './config/dialogues.json'
olddialogues.dialogues = oldconfig.dialogues
await fs.promises.writeFile(olddialogues.file, JSON.stringify(olddialogues, null, 2))
delete oldconfig.dialogues
oldbadwords={}
if oldconfig.ban
if oldconfig.ban.badword_level0
oldbadwords.level0 = oldconfig.ban.badword_level0
if oldconfig.ban.badword_level1
oldbadwords.level1 = oldconfig.ban.badword_level1
if oldconfig.ban.badword_level2
oldbadwords.level2 = oldconfig.ban.badword_level2
if oldconfig.ban.badword_level3
oldbadwords.level3 = oldconfig.ban.badword_level3
if not _.isEmpty(oldbadwords)
oldbadwords.file = './config/badwords.json'
await fs.promises.writeFile(oldbadwords.file, JSON.stringify(oldbadwords, null, 2))
delete oldconfig.ban.badword_level0
delete oldconfig.ban.badword_level1
delete oldconfig.ban.badword_level2
delete oldconfig.ban.badword_level3
if not _.isEmpty(oldconfig)
# log.info oldconfig
await fs.promises.writeFile('./config/config.json', JSON.stringify(oldconfig, null, 2))
log.info 'imported old config from config.user.json'
await fs.promises.rename('./config.user.json', './config.user.bak')
catch e
log.info e unless e.code == 'ENOENT'
auth = global.auth = require './ygopro-auth.js'
ygopro = global.ygopro = require './ygopro.js'
roomlist = null
settings = {}
tips = null
words = null
vip_info = null
dialogues = null
badwords = null
lflists = global.lflists = []
real_windbot_server_ip = null
long_resolve_cards = []
ReplayParser = null
athleticChecker = null
users_cache = {}
geoip = null
dataManager = null
disconnect_list = {} # {old_client, old_server, room_id, timeout, deckbuf}
challonge = null
challonge_cache = {
participants: null
matches: null
}
challonge_queue_callbacks = {
participants: []
matches: []
}
is_challonge_requesting = {
participants: null
matches: null
}
get_callback = () ->
replaced_index = () ->
refresh_challonge_cache = () ->
class ResolveData
constructor: (@func) ->
resolved: false
resolve: (err, data) ->
if @resolved
return false
@resolved = true
@func(err, data)
return true
loadLFList = (path) ->
try try
config = loadJSON('./config/config.json') for list in fs.promises.readFile(path, 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
init = () ->
await createDirectoryIfNotExists("./config")
await importOldConfig()
defaultConfig = await loadJSONAsync('./data/default_config.json')
if await checkFileExists("./config/config.json")
try
config = await loadJSONAsync('./config/config.json')
catch e catch e
console.error("Failed reading config: ", e.toString()) console.error("Failed reading config: ", e.toString())
process.exit(1) process.exit(1)
else else
config = {} config = {}
settings = global.settings = merge(defaultConfig, config, { arrayMerge: (destination, source) -> source })
settings = global.settings = merge(default_config, config, { arrayMerge: (destination, source) -> source }) #import old configs
imported = false
auth = global.auth = require './ygopro-auth.js' #reset http.quick_death_rule from true to 1
if settings.modules.http.quick_death_rule == true
#import old configs
imported = false
#reset http.quick_death_rule from true to 1
if settings.modules.http.quick_death_rule == true
settings.modules.http.quick_death_rule = 1 settings.modules.http.quick_death_rule = 1
imported = true imported = true
#import the old passwords to new admin user system #import the old passwords to new admin user system
if settings.modules.http.password if settings.modules.http.password
auth.add_user("olduser", settings.modules.http.password, true, { await auth.add_user("olduser", settings.modules.http.password, true, {
"get_rooms": true, "get_rooms": true,
"shout": true, "shout": true,
"stop": true, "stop": true,
...@@ -285,8 +336,8 @@ if settings.modules.http.password ...@@ -285,8 +336,8 @@ if settings.modules.http.password
}) })
delete settings.modules.http.password delete settings.modules.http.password
imported = true imported = true
if settings.modules.tournament_mode.password if settings.modules.tournament_mode.password
auth.add_user("tournament", settings.modules.tournament_mode.password, true, { await auth.add_user("tournament", settings.modules.tournament_mode.password, true, {
"duel_log": true, "duel_log": true,
"download_replay": true, "download_replay": true,
"clear_duel_log": true, "clear_duel_log": true,
...@@ -295,154 +346,127 @@ if settings.modules.tournament_mode.password ...@@ -295,154 +346,127 @@ if settings.modules.tournament_mode.password
}) })
delete settings.modules.tournament_mode.password delete settings.modules.tournament_mode.password
imported = true imported = true
if settings.modules.pre_util.password if settings.modules.pre_util.password
auth.add_user("pre", settings.modules.pre_util.password, true, { await auth.add_user("pre", settings.modules.pre_util.password, true, {
"pre_dashboard": true "pre_dashboard": true
}) })
delete settings.modules.pre_util.password delete settings.modules.pre_util.password
imported = true imported = true
if settings.modules.update_util.password if settings.modules.update_util.password
auth.add_user("update", settings.modules.update_util.password, true, { await auth.add_user("update", settings.modules.update_util.password, true, {
"update_dashboard": true "update_dashboard": true
}) })
delete settings.modules.update_util.password delete settings.modules.update_util.password
imported = true imported = true
#import the old enable_priority hostinfo #import the old enable_priority hostinfo
if settings.hostinfo.enable_priority or settings.hostinfo.enable_priority == false if settings.hostinfo.enable_priority or settings.hostinfo.enable_priority == false
if settings.hostinfo.enable_priority if settings.hostinfo.enable_priority
settings.hostinfo.duel_rule = 3 settings.hostinfo.duel_rule = 3
else else
settings.hostinfo.duel_rule = 5 settings.hostinfo.duel_rule = 5
delete settings.hostinfo.enable_priority delete settings.hostinfo.enable_priority
imported = true imported = true
#import the old Challonge api key option #import the old Challonge api key option
if settings.modules.challonge.api_key if settings.modules.challonge.api_key
settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key
delete settings.modules.challonge.api_key delete settings.modules.challonge.api_key
imported = true imported = true
#import the old random_duel.blank_pass_match option #import the old random_duel.blank_pass_match option
if settings.modules.random_duel.blank_pass_match == true if settings.modules.random_duel.blank_pass_match == true
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":true,"T":false} settings.modules.random_duel.blank_pass_modes = {"S":true,"M":true,"T":false}
delete settings.modules.random_duel.blank_pass_match delete settings.modules.random_duel.blank_pass_match
imported = true imported = true
if settings.modules.random_duel.blank_pass_match == false if settings.modules.random_duel.blank_pass_match == false
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":false,"T":false} settings.modules.random_duel.blank_pass_modes = {"S":true,"M":false,"T":false}
delete settings.modules.random_duel.blank_pass_match delete settings.modules.random_duel.blank_pass_match
imported = true imported = true
#finish #finish
if imported if imported
setting_save(settings) await setting_save(settings)
# 读取数据
# 读取数据 default_data = await loadJSONAsync('./data/default_data.json')
default_data = loadJSON('./data/default_data.json') try
try tips = global.tips = await loadJSONAsync('./config/tips.json')
tips = global.tips = loadJSON('./config/tips.json') catch
if !tips.tips_zh
tips.tips_zh = []
setting_save(tips);
catch
tips = global.tips = default_data.tips tips = global.tips = default_data.tips
setting_save(tips) await setting_save(tips)
try try
words = global.words = loadJSON('./config/words.json') dialogues = global.dialogues = await loadJSONAsync('./config/dialogues.json')
catch catch
words = global.words = default_data.words
setting_save(words)
try
dialogues = global.dialogues = loadJSON('./config/dialogues.json')
if !dialogues.dialogues_custom
dialogues.dialogues_custom = {}
setting_save(dialogues);
catch
dialogues = global.dialogues = default_data.dialogues dialogues = global.dialogues = default_data.dialogues
setting_save(dialogues) await setting_save(dialogues)
try try
badwords = global.badwords = loadJSON('./config/badwords.json') words = global.words = await loadJSONAsync('./config/words.json')
catch catch
words = global.words = default_data.words
await setting_save(words)
try
vip_info = global.vip_info = await loadJSONAsync('./config/vip_info.json')
catch
vip_info = global.vip_info = default_data.vip_info
await setting_save(vip_info)
try
badwords = global.badwords = await loadJSONAsync('./config/badwords.json')
catch
badwords = global.badwords = default_data.badwords badwords = global.badwords = default_data.badwords
setting_save(badwords) await setting_save(badwords)
try if settings.modules.chat_color.enabled
duel_log = global.duel_log = loadJSON('./config/duel_log.json') try
catch chat_color = global.chat_color = await loadJSONAsync('./config/chat_color.json')
duel_log = global.duel_log = default_data.duel_log catch
setting_save(duel_log)
try
chat_color = global.chat_color = loadJSON('./config/chat_color.json')
catch
chat_color = global.chat_color = default_data.chat_color chat_color = global.chat_color = default_data.chat_color
setting_save(chat_color) await setting_save(chat_color)
try try
vip_info = global.vip_info = loadJSON('./config/vip_info.json') cppversion = parseInt(await fs.promises.readFile('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
catch await setting_change(settings, "version", cppversion)
vip_info = global.vip_info = default_data.vip_info
setting_save(vip_info)
try
cppversion = parseInt(fs.readFileSync('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
setting_change(settings, "version", cppversion)
log.info "ygopro version 0x"+settings.version.toString(16), "(from source code)" log.info "ygopro version 0x"+settings.version.toString(16), "(from source code)"
catch catch
#settings.version = settings.version_default #settings.version = settings.version_default
log.info "ygopro version 0x"+settings.version.toString(16), "(from config)" log.info "ygopro version 0x"+settings.version.toString(16), "(from config)"
# load the lflist of current date # load the lflist of current date
lflists = global.lflists = [] await loadLFList('ygopro/expansions/lflist.conf')
# expansions/lflist await loadLFList('ygopro/lflist.conf')
try
for list in fs.readFileSync('ygopro/expansions/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
# lflist
try
for list in fs.readFileSync('ygopro/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
if settings.modules.windbot.enabled if settings.modules.windbot.enabled
windbots = global.windbots = loadJSON(settings.modules.windbot.botlist).windbots windbots = global.windbots = await loadJSONAsync(settings.modules.windbot.botlist).windbots
real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip
if !settings.modules.windbot.server_ip.includes("127.0.0.1") if !settings.modules.windbot.server_ip.includes("127.0.0.1")
dns = require('dns') dns = require('dns')
dns.lookup(settings.modules.windbot.server_ip,(err,addr) -> real_windbot_server_ip = global.real_windbot_server_ip = await util.promisify(dns.lookup)(settings.modules.windbot.server_ip)
if(!err) if settings.modules.heartbeat_detection.enabled
real_windbot_server_ip = global.real_windbot_server_ip = addr long_resolve_cards = global.long_resolve_cards = await loadJSONAsync('./data/long_resolve_cards.json')
)
if settings.modules.heartbeat_detection.enabled
long_resolve_cards = global.long_resolve_cards = loadJSON('./data/long_resolve_cards.json')
if settings.modules.tournament_mode.enable_recover if settings.modules.tournament_mode.enable_recover
ReplayParser = global.ReplayParser = require "./Replay.js" ReplayParser = global.ReplayParser = require "./Replay.js"
if settings.modules.athletic_check.enabled if settings.modules.athletic_check.enabled
AthleticChecker = require("./athletic-check.js").AthleticChecker AthleticChecker = require("./athletic-check.js").AthleticChecker
athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check) athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check)
# 组件 if settings.modules.http.websocket_roomlist
ygopro = global.ygopro = require './ygopro.js' roomlist = global.roomlist = require './roomlist.js'
roomlist = global.roomlist = require './roomlist.js' if settings.modules.http.websocket_roomlist if settings.modules.i18n.auto_pick
if settings.modules.i18n.auto_pick
geoip = require('geoip-country-lite') geoip = require('geoip-country-lite')
if settings.modules.mysql.enabled
# cache users of mycard login
users_cache = {}
if settings.modules.mysql.enabled
DataManager = require('./data-manager/DataManager.js').DataManager DataManager = require('./data-manager/DataManager.js').DataManager
dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log) dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log)
dataManager.init().then(() -> log.info("Database ready.")) await dataManager.init()
else else
log.warn("Some functions may be limited without MySQL .")
if settings.modules.cloud_replay.enabled if settings.modules.cloud_replay.enabled
settings.modules.cloud_replay.enabled = false settings.modules.cloud_replay.enabled = false
setting_save(settings) await setting_save(settings)
log.warn("Cloud replay cannot be enabled because no MySQL.") log.warn("Cloud replay cannot be enabled because no MySQL.")
if settings.modules.enable_recover.enabled
settings.modules.enable_recover.enabled = false
await setting_save(settings)
log.warn("Recover mode cannot be enabled because no MySQL.")
if settings.modules.chat_color.enabled
settings.modules.chat_color.enabled = false
await setting_save(settings)
log.warn("Chat color cannot be enabled because no MySQL.")
if settings.modules.mycard.enabled if settings.modules.mycard.enabled
pgClient = require('pg').Client pgClient = require('pg').Client
pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database) pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database)
pg_client.on 'error', (err) -> pg_client.on 'error', (err) ->
...@@ -463,35 +487,22 @@ if settings.modules.mycard.enabled ...@@ -463,35 +487,22 @@ if settings.modules.mycard.enabled
log.info "loading mycard user..." log.info "loading mycard user..."
pg_client.connect() pg_client.connect()
if settings.modules.arena_mode.enabled and settings.modules.arena_mode.init_post.enabled if settings.modules.arena_mode.enabled and settings.modules.arena_mode.init_post.enabled
request.post { url : settings.modules.arena_mode.init_post.url , qs : { postData = qs.stringify({
ak: settings.modules.arena_mode.init_post.accesskey, ak: settings.modules.arena_mode.init_post.accesskey,
arena: settings.modules.arena_mode.mode arena: settings.modules.arena_mode.mode
}}, (error, response, body)=> })
if error try
log.warn 'ARENA INIT POST ERROR', error await axios.post(settings.modules.arena_mode.init_post.url + "?" + postData, {
else responseType: "json"
if response.statusCode >= 400 })
log.warn 'ARENA INIT POST FAIL', response.statusCode, response.statusMessage, body catch e
#else log.warn 'ARENA INIT POST ERROR', e
# log.info 'ARENA INIT POST OK', response.statusCode, response.statusMessage
return
class ResolveData
constructor: (@func) ->
resolved: false
resolve: (err, data) ->
if @resolved
return false
@resolved = true
@func(err, data)
return true
if settings.modules.challonge.enabled if settings.modules.challonge.enabled
challonge_module_name = 'challonge' challonge_module_name = 'challonge'
if settings.modules.challonge.use_custom_module if settings.modules.challonge.use_custom_module
challonge_module_name = settings.modules.challonge.use_custom_module challonge_module_name = settings.modules.challonge.use_custom_module
challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options) challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options)
if settings.modules.challonge.cache_ttl
challonge_cache = { challonge_cache = {
participants: null participants: null
matches: null matches: null
...@@ -544,46 +555,164 @@ if settings.modules.challonge.enabled ...@@ -544,46 +555,164 @@ if settings.modules.challonge.enabled
challonge_cache.matches = null challonge_cache.matches = null
return return
refresh_challonge_cache() refresh_challonge_cache()
# challonge.participants._index({
# id: settings.modules.challonge.tournament_id,
# callback: (() ->
# challonge.matches._index({
# id: settings.modules.challonge.tournament_id,
# callback: (() ->
# return
# )
# })
# return
# )
# })
if settings.modules.challonge.cache_ttl if settings.modules.challonge.cache_ttl
setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl) setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl)
if settings.modules.vip.enabled if settings.modules.tips.get
load_tips()
if settings.modules.tips.get_zh
load_tips_zh()
if settings.modules.tips.enabled
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
return
, 30000
if settings.modules.dialogues.enabled and settings.modules.dialogues.get
load_dialogues()
if settings.modules.dialogues.enabled and settings.modules.dialogues.get_custom
load_dialogues_custom()
if settings.modules.words.get
load_words()
if settings.modules.random_duel.post_match_scores
setInterval(()->
scores_pair = _.pairs ROOM_players_scores
scores_by_lose = _.sortBy(scores_pair, (score)-> return score[1].lose).reverse() # 败场由高到低
scores_by_win = _.sortBy(scores_by_lose, (score)-> return score[1].win).reverse() # 然后胜场由低到高,再逆转,就是先排胜场再排败场
scores = _.first(scores_by_win, 10)
#log.info scores
request.post { url : settings.modules.random_duel.post_match_scores , form : {
accesskey: settings.modules.random_duel.post_match_accesskey,
rank: JSON.stringify(scores)
}}, (error, response, body)=>
if error
log.warn 'RANDOM SCORE POST ERROR', error
else
if response.statusCode != 204 and response.statusCode != 200
log.warn 'RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body
#else
# log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
return
return
, 60000)
if settings.modules.random_duel.enabled
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() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}")
return
, 1000
if settings.modules.mycard.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.arena 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() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
return
if true # settings.modules.arena_mode.punish_quit_before_match
for room in ROOM_all when room and room.arena and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.get_playing_player().length < 2
player = room.get_playing_player()[0]
if player and player.join_time and !player.arena_quit_free
waited_time = moment() - player.join_time
if waited_time >= 30000
ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE)
player.arena_quit_free = true
else if waited_time >= 5000 and waited_time < 6000
ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE)
return
, 1000
if settings.modules.heartbeat_detection.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and (room.hostinfo.time_limit == 0 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING) and !room.windbot
for player in room.get_playing_player() when player and (room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING or player.selected_preduel)
CLIENT_heartbeat_register(player, true)
return
, settings.modules.heartbeat_detection.interval
if settings.modules.windbot.enabled and settings.modules.windbot.spawn
spawn_windbot()
setInterval ()->
current_time = moment()
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.auto_death and !room.auto_death_triggered and current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death
room.auto_death_triggered = true
room.start_death()
, 1000
if settings.modules.vip.enabled
for k,v of vip_info.cdkeys when v.length == 0 for k,v of vip_info.cdkeys when v.length == 0
VIP_generate_cdkeys(k, settings.modules.vip.generate_count) VIP_generate_cdkeys(k, settings.modules.vip.generate_count)
net.createServer(netRequestHandler).listen settings.port, ->
log.info "server started", settings.port
return
if settings.modules.stop
log.info "NOTE: server not open due to config, ", settings.modules.stop
http_server = http.createServer(httpRequestListener)
http_server.listen settings.modules.http.port
if settings.modules.http.ssl.enabled
https = require 'https'
options =
cert: await fs.promises.readFile(settings.modules.http.ssl.cert)
key: await fs.promises.readFile(settings.modules.http.ssl.key)
https_server = https.createServer(options, httpRequestListener)
if settings.modules.http.websocket_roomlist and roomlist
roomlist.init https_server, ROOM_all
https_server.listen settings.modules.http.ssl.port
mkdirList = [
"./plugins",
settings.modules.tournament_mode.deck_path,
settings.modules.tournament_mode.replay_path,
settings.modules.tournament_mode.log_save_path,
settings.modules.deck_log.local
]
for path in mkdirList
await createDirectoryIfNotExists(path)
plugin_list = await fs.promises.readdir("./plugins")
for plugin_filename in plugin_list
plugin_path = process.cwd() + "/plugins/" + plugin_filename
require(plugin_path)
log.info("Plugin loaded:", plugin_filename)
# 获取可用内存 # 获取可用内存
memory_usage = global.memory_usage = 0 memory_usage = global.memory_usage = 0
get_memory_usage = get_memory_usage = ()-> get_memory_usage = global.get_memory_usage = ()->
prc_free = exec("free") percentUsed = os.freemem() * os.totalmem() * 100
prc_free.stdout.on 'data', (data)->
lines = data.toString().split(/\n/g)
line = lines[0].split(/\s+/)
new_free = if line[6] == 'available' then true else false
line = lines[1].split(/\s+/)
total = parseInt(line[1], 10)
free = parseInt(line[3], 10)
buffers = parseInt(line[5], 10)
if new_free
actualFree = parseInt(line[6], 10)
else
cached = parseInt(line[6], 10)
actualFree = free + buffers + cached
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2))
memory_usage = global.memory_usage = percentUsed memory_usage = global.memory_usage = percentUsed
return return
return
get_memory_usage() get_memory_usage()
setInterval(get_memory_usage, 3000) setInterval(get_memory_usage, 3000)
...@@ -623,7 +752,7 @@ ROOM_kick = (name, callback)-> ...@@ -623,7 +752,7 @@ ROOM_kick = (name, callback)->
done() done()
return return
found = true found = true
room.termiate() room.terminate()
done() done()
, (err)-> , (err)->
callback(null, found) callback(null, found)
...@@ -667,28 +796,6 @@ ROOM_player_get_score = global.ROOM_player_get_score = (player)-> ...@@ -667,28 +796,6 @@ ROOM_player_get_score = global.ROOM_player_get_score = (player)->
return "${random_score_part1}#{player.name} ${random_score_part2} #{Math.ceil(score.win/total*100)}${random_score_part3} #{Math.ceil(score.flee/total*100)}${random_score_part4}" return "${random_score_part1}#{player.name} ${random_score_part2} #{Math.ceil(score.win/total*100)}${random_score_part3} #{Math.ceil(score.flee/total*100)}${random_score_part4}"
return return
if settings.modules.random_duel.post_match_scores
setInterval(()->
scores_pair = _.pairs ROOM_players_scores
scores_by_lose = _.sortBy(scores_pair, (score)-> return score[1].lose).reverse() # 败场由高到低
scores_by_win = _.sortBy(scores_by_lose, (score)-> return score[1].win).reverse() # 然后胜场由低到高,再逆转,就是先排胜场再排败场
scores = _.first(scores_by_win, 10)
#log.info scores
request.post { url : settings.modules.random_duel.post_match_scores , form : {
accesskey: settings.modules.random_duel.post_match_accesskey,
rank: JSON.stringify(scores)
}}, (error, response, body)=>
if error
log.warn 'RANDOM SCORE POST ERROR', error
else
if response.statusCode != 204 and response.statusCode != 200
log.warn 'RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body
#else
# log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
return
return
, 60000)
ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player_ip)-> ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player_ip)->
uname=name.toUpperCase() uname=name.toUpperCase()
if settings.modules.windbot.enabled and (uname[0...2] == 'AI' or (!settings.modules.random_duel.enabled and uname == '')) if settings.modules.windbot.enabled and (uname[0...2] == 'AI' or (!settings.modules.random_duel.enabled and uname == ''))
...@@ -700,7 +807,12 @@ ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player ...@@ -700,7 +807,12 @@ ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player
else if memory_usage >= 90 else if memory_usage >= 90
return null return null
else else
return new Room(name) room = new Room(name)
if room.recover_duel_log_id
success = await room.initialize_recover()
if !success
return {"error": "${cloud_replay_no}"}
return room
ROOM_find_or_create_random = global.ROOM_find_or_create_random = (type, player_ip)-> ROOM_find_or_create_random = global.ROOM_find_or_create_random = (type, player_ip)->
if settings.modules.mysql.enabled if settings.modules.mysql.enabled
...@@ -1087,9 +1199,6 @@ CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = (client, deckbuf) -> ...@@ -1087,9 +1199,6 @@ CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = (client, deckbuf) ->
CLIENT_reconnect_unregister(client, true) CLIENT_reconnect_unregister(client, true)
return return
if settings.modules.reconnect.enabled
disconnect_list = {} # {old_client, old_server, room_id, timeout, deckbuf}
CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = (client) -> CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = (client) ->
if !settings.modules.heartbeat_detection.enabled or !client.heartbeat_timeout if !settings.modules.heartbeat_detection.enabled or !client.heartbeat_timeout
return false return false
...@@ -1317,14 +1426,7 @@ class Room ...@@ -1317,14 +1426,7 @@ class Room
@recovered = true @recovered = true
@recovering = true @recovering = true
@recover_from_turn = parseInt(param[4]) @recover_from_turn = parseInt(param[4])
duel_log_id = parseInt(param[3]) @recover_duel_log_id = parseInt(param[3])
@recover_duel_log = _.find(duel_log.duel_log, (duel) ->
return duel.id == duel_log_id and duel.roommode != 2 and duel.players[0].deck
)
if !@recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replay_filename)
@error = "${cloud_replay_no}"
return
@recover_replay = ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replay_filename)
@recover_buffers = [[], [], [], []] @recover_buffers = [[], [], [], []]
@welcome = "${recover_hint}" @welcome = "${recover_hint}"
...@@ -1335,12 +1437,16 @@ class Room ...@@ -1335,12 +1437,16 @@ class Room
if (settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player) or (@hostinfo.mode == 1 and settings.modules.replay_delay) if (settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player) or (@hostinfo.mode == 1 and settings.modules.replay_delay)
@hostinfo.replay_mode |= 0x2 @hostinfo.replay_mode |= 0x2
if !@recovered
@spawn()
spawn: (firstSeed) ->
param = [0, @hostinfo.lflist, @hostinfo.rule, @hostinfo.mode, @hostinfo.duel_rule, param = [0, @hostinfo.lflist, @hostinfo.rule, @hostinfo.mode, @hostinfo.duel_rule,
(if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck then 'T' else 'F'), (if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck then 'T' else 'F'),
@hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode] @hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode]
if @recovered if firstSeed
param.push(@recover_replay.header.seed) param.push(firstSeed)
seeds = getSeedTimet(2) seeds = getSeedTimet(2)
param.push(seeds[i]) for i in [0...2] param.push(seeds[i]) for i in [0...2]
else else
...@@ -1489,6 +1595,23 @@ class Room ...@@ -1489,6 +1595,23 @@ class Room
roomlist.delete this if !@windbot and @established and settings.modules.http.websocket_roomlist roomlist.delete this if !@windbot and @established and settings.modules.http.websocket_roomlist
return return
initialize_recover: ->
@recover_duel_log = await dataManager.getDuelLogFromId(@recover_duel_log_id)
#console.log(@recover_duel_log, fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName))
if !@recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName)
@terminate()
return false
try
@recover_replay = await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName)
@spawn(@recover_replay.header.seed)
return true
catch e
log.warn("LOAD RECOVER REPLAY FAIL", e.toString())
@terminate()
return false
get_playing_player: -> get_playing_player: ->
playing_player = [] playing_player = []
_.each @players, (player)-> _.each @players, (player)->
...@@ -1680,19 +1803,22 @@ class Room ...@@ -1680,19 +1803,22 @@ class Room
ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE)
return true return true
termiate: -> terminate: ->
if @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN if @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN
@scores[@dueling_players[0].name_vpass] = 0 @scores[@dueling_players[0].name_vpass] = 0
@scores[@dueling_players[1].name_vpass] = 0 @scores[@dueling_players[1].name_vpass] = 0
@kicked = true @kicked = true
@send_replays() @send_replays()
if @process
try
@process.kill() @process.kill()
catch e
@delete() @delete()
finish_recover: (fail) -> finish_recover: (fail) ->
if fail if fail
ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED)
@termiate() @terminate()
else else
ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE)
@recovering = false @recovering = false
...@@ -1715,7 +1841,7 @@ class Room ...@@ -1715,7 +1841,7 @@ class Room
await return await return
# 网络连接 # 网络连接
net.createServer (client) -> netRequestHandler = (client) ->
client.ip = client.remoteAddress client.ip = client.remoteAddress
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip)) client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
...@@ -1895,12 +2021,6 @@ net.createServer (client) -> ...@@ -1895,12 +2021,6 @@ net.createServer (client) ->
return return
return return
.listen settings.port, ->
log.info "server started", settings.port
return
if settings.modules.stop
log.info "NOTE: server not open due to config, ", settings.modules.stop
deck_name_match = global.deck_name_match = (deck_name, player_name) -> deck_name_match = global.deck_name_match = (deck_name, player_name) ->
if deck_name == player_name or deck_name == player_name + ".ydk" or deck_name == player_name + ".ydk.ydk" if deck_name == player_name or deck_name == player_name + ".ydk" or deck_name == player_name + ".ydk.ydk"
...@@ -1991,26 +2111,14 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -1991,26 +2111,14 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
else if info.pass.toUpperCase()=="RC" and settings.modules.tournament_mode.enable_recover else if info.pass.toUpperCase()=="RC" and settings.modules.tournament_mode.enable_recover
ygopro.stoc_send_chat(client,"${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client,"${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE)
available_logs = duel_log.duel_log.filter((duel) -> available_logs = await dataManager.getDuelLogFromRecoverSearch(client.name_vpass)
return duel.id and duel.players[0].deck and duel.roommode != 2 and _.any(duel.players, (player) -> for duelLog in available_logs
return player.real_name == client.name_vpass ygopro.stoc_send_chat(client, duelLog.getViewString(), ygopro.constants.COLORS.BABYBLUE)
)
).slice(0, 8)
_.each(available_logs, (duel) ->
player_names = duel.players[0].real_name.split("$")[0] + (if duel.players[2] then "+" + duel.players[2].real_name.split("$")[0] else "") +
" VS " +
(if duel.players[1] then duel.players[1].real_name.split("$")[0] else "AI") +
(if duel.players[3] then "+" + duel.players[3].real_name.split("$")[0] else "")
ygopro.stoc_send_chat(client,"<#{duel.id}> #{player_names} #{duel.time}", ygopro.constants.COLORS.BABYBLUE)
)
# 强行等待异步执行完毕_(:з」∠)_
setTimeout (()->
ygopro.stoc_send client, 'ERROR_MSG',{ ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1 msg: 1
code: 9 code: 9
} }
CLIENT_kick(client) CLIENT_kick(client)
return), 500
else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled
replay_id=info.pass.split("#")[1] replay_id=info.pass.split("#")[1]
...@@ -2264,6 +2372,8 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2264,6 +2372,8 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
if(err) if(err)
ygopro.stoc_die(client, err) ygopro.stoc_die(client, err)
return return
create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit) create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit)
) )
...@@ -2542,65 +2652,12 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)-> ...@@ -2542,65 +2652,12 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
await return await return
# 登场台词 # 登场台词
load_words = global.load_words = (callback) -> load_dialogues = global.load_dialogues = () ->
request return await loadRemoteData(dialogues, "dialogues", settings.modules.dialogues.get)
url: settings.modules.words.get load_dialogues_custom = global.load_dialogues_custom = () ->
json: true return await loadRemoteData(dialogues, "dialogues_custom", settings.modules.dialogues.get_custom)
, (error, response, body)-> load_words = global.load_words = () ->
if _.isString body return await loadRemoteData(words, "words", settings.modules.words.get)
log.warn "words bad json", body
else if error or !body
log.warn 'words error', error, response
else
setting_change(words, "words", body)
log.info "words loaded", _.size words.words
if callback
callback(error, body)
return
return
if settings.modules.words.get
load_words()
load_dialogues = global.load_dialogues = (callback) ->
request
url: settings.modules.dialogues.get
json: true
, (error, response, body)->
if _.isString body
log.warn "dialogues bad json", body
else if error or !body
log.warn 'dialogues error', error, response
else
setting_change(dialogues, "dialogues", body)
log.info "dialogues loaded", _.size dialogues.dialogues
if callback
callback(error, body)
return
await return
load_dialogues_custom = global.load_dialogues_custom = (callback) ->
request
url: settings.modules.dialogues.get_custom
json: true
, (error, response, body)->
if _.isString body
log.warn "custom dialogues bad json", body
else if error or !body
log.warn 'custom dialogues error', error, response
else
setting_change(dialogues, "dialogues_custom", body)
log.info "custom dialogues loaded", _.size dialogues.dialogues_custom
if callback
callback(error, body)
return
return
if settings.modules.dialogues.enabled and settings.modules.dialogues.get
load_dialogues()
if settings.modules.dialogues.enabled and settings.modules.dialogues.get_custom
load_dialogues_custom()
ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
...@@ -3081,50 +3138,28 @@ ygopro.stoc_send_random_tip_to_room = (room)-> ...@@ -3081,50 +3138,28 @@ ygopro.stoc_send_random_tip_to_room = (room)->
ygopro.stoc_send_random_tip(player) ygopro.stoc_send_random_tip(player)
await return await return
load_tips = global.load_tips = (callback)-> loadRemoteData = global.loadRemoteData = (loadObject, name, url)->
request try
url: settings.modules.tips.get body = (await axios.get(url, {
json: true responseType: "json"
, (error, response, body)-> })).data
if _.isString body
log.warn "tips bad json", body
else if error or !body
log.warn 'tips error', error, response
else
setting_change(tips, "tips", body)
log.info "tips loaded", tips.tips.length
if callback
callback(error, body)
return
await return
load_tips_zh = global.load_tips_zh = (callback)->
request
url: settings.modules.tips.get_zh
json: true
, (error, response, body)->
if _.isString body if _.isString body
log.warn "zh tips bad json", body log.warn "#{name} bad json", body
else if error or !body return false
log.warn 'zh tips error', error, response if !body
else log.warn "#{name} empty", body
setting_change(tips, "tips_zh", body) return false
log.info "zh tips loaded", tips.tips_zh.length await setting_change(loadObject, name, body)
if callback log.info "#{name} loaded"
callback(error, body) return true
return catch e
await return log.warn "#{name} error", e
return false
if settings.modules.tips.enabled and settings.modules.tips.get load_tips = global.load_tips = ()->
load_tips() return await loadRemoteData(tips, "tips", settings.modules.tips.get)
if settings.modules.tips.enabled and settings.modules.tips.get_zh load_tips_zh = global.load_tips_zh = ()->
load_tips_zh() return await loadRemoteData(tips, "tips_zh", settings.modules.tips.get_zh)
if settings.modules.tips.enabled
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
return
, 30000
ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
...@@ -3556,13 +3591,14 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)-> ...@@ -3556,13 +3591,14 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
room.last_active_time = moment() room.last_active_time = moment()
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering
recover_player_data = _.find(room.recover_duel_log.players, (player) -> recover_player_data = _.find(room.recover_duel_log.players, (player) ->
return player.real_name == client.name_vpass and _.isEqual(buffer, Buffer.from(player.deckbuf, "base64")) return player.realName == client.name_vpass and buffer.toString("base64") == player.startDeckBuffer
) )
if recover_player_data if recover_player_data
struct.set("mainc", recover_player_data.deck.main.length) recoveredDeck = recover_player_data.getCurrentDeck()
struct.set("sidec", recover_player_data.deck.side.length) struct.set("mainc", recoveredDeck.main.length)
struct.set("deckbuf", recover_player_data.deck.main.concat(recover_player_data.deck.side)) struct.set("sidec", recoveredDeck.side.length)
if recover_player_data.is_first struct.set("deckbuf", recoveredDeck.main.concat(recoveredDeck.side))
if recover_player_data.isFirst
room.determine_firstgo = client room.determine_firstgo = client
else else
struct.set("mainc", 1) struct.set("mainc", 1)
...@@ -3812,10 +3848,9 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)-> ...@@ -3812,10 +3848,9 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
if !room.replays[room.duel_count - 1] if !room.replays[room.duel_count - 1]
# console.log("Replay saved: ", room.duel_count - 1, client.pos) # console.log("Replay saved: ", room.duel_count - 1, client.pos)
room.replays[room.duel_count - 1] = buffer room.replays[room.duel_count - 1] = buffer
if settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.replay_safe or settings.modules.tournament_mode.enable_recover if settings.modules.mysql.enabled
if client.pos == 0 if client.pos == 0
dueltime=moment().format('YYYY-MM-DD HH-mm-ss') replay_filename=moment().format("YYYY-MM-DD HH-mm-ss")
replay_filename=dueltime
if room.hostinfo.mode != 2 if room.hostinfo.mode != 2
for player,i in room.dueling_players for player,i in room.dueling_players
replay_filename=replay_filename + (if i > 0 then " VS " else " ") + player.name replay_filename=replay_filename + (if i > 0 then " VS " else " ") + player.name
...@@ -3823,102 +3858,34 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)-> ...@@ -3823,102 +3858,34 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
for player,i in room.dueling_players for player,i in room.dueling_players
replay_filename=replay_filename + (if i > 0 then (if i == 2 then " VS " else " & ") else " ") + player.name replay_filename=replay_filename + (if i > 0 then (if i == 2 then " VS " else " & ") else " ") + player.name
replay_filename=replay_filename.replace(/[\/\\\?\*]/g, '_')+".yrp" replay_filename=replay_filename.replace(/[\/\\\?\*]/g, '_')+".yrp"
duellog = { playerInfos = room.dueling_players.map((player) ->
id: duel_log.duel_log.length + 1, return {
time: dueltime, name: player.name
name: room.name + (if settings.modules.tournament_mode.show_info then (" (Duel:" + room.duel_count + ")") else ""), pos: player.pos
roomid: room.process_pid.toString(), realName: player.name_vpass
cloud_replay_id: "R#"+room.cloud_replay_id, startDeckBuffer: player.start_deckbuf
replay_filename: replay_filename,
roommode: room.hostinfo.mode,
players: (for player in room.dueling_players
real_name: player.name_vpass,
deckbuf: player.start_deckbuf.toString("base64"),
deck: { deck: {
main: player.main, main: player.main,
side: player.side side: player.side
} }
pos: player.pos isFirst: player.is_first
is_first: player.is_first
name: player.name + (if settings.modules.tournament_mode.show_ip and !player.is_local then (" (IP: " + player.ip.slice(7) + ")") else "") + (if settings.modules.tournament_mode.show_info and not (room.hostinfo.mode == 2 and player.pos % 2 > 0) then (" (Score:" + room.scores[player.name_vpass] + " LP:" + (if player.lp? then player.lp else room.hostinfo.start_lp) + (if room.hostinfo.mode != 2 then (" Cards:" + (if player.card_count? then player.card_count else room.hostinfo.start_hand)) else "") + ")") else ""),
winner: player.pos == room.winner winner: player.pos == room.winner
) ip: player.ip
score: room.scores[player.name_vpass]
lp: if player.lp? then player.lp else room.hostinfo.start_lp
cardCount: if player.card_count? then player.card_count else room.hostinfo.start_hand
} }
duel_log.duel_log.unshift duellog )
setting_save(duel_log)
fs.writeFile(settings.modules.tournament_mode.replay_path + replay_filename, buffer, (err)-> fs.writeFile(settings.modules.tournament_mode.replay_path + replay_filename, buffer, (err)->
if err then log.warn "SAVE REPLAY ERROR", replay_filename, err if err then log.warn "SAVE REPLAY ERROR", replay_filename, err
) )
dataManager.saveDuelLog(room.name, room.process_pid.toString(), room.cloud_replay_id, replay_filename, room.hostinfo.mode, room.duel_count, playerInfos) # no synchronize here because too slow
if settings.modules.cloud_replay.enabled and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.replay_safe if settings.modules.cloud_replay.enabled and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.replay_safe
ygopro.stoc_send_chat(client, "${cloud_replay_delay_part1}R##{room.cloud_replay_id}${cloud_replay_delay_part2}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${cloud_replay_delay_part1}R##{room.cloud_replay_id}${cloud_replay_delay_part2}", ygopro.constants.COLORS.BABYBLUE)
await return settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player or settings.modules.replay_delay and room.hostinfo.mode == 1 await return settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player or settings.modules.replay_delay and room.hostinfo.mode == 1
else else
await return settings.modules.replay_delay and room.hostinfo.mode == 1 await return settings.modules.replay_delay and room.hostinfo.mode == 1
if settings.modules.random_duel.enabled
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() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}")
return
, 1000
if settings.modules.mycard.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.arena 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() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
return
if true # settings.modules.arena_mode.punish_quit_before_match
for room in ROOM_all when room and room.arena and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.get_playing_player().length < 2
player = room.get_playing_player()[0]
if player and player.join_time and !player.arena_quit_free
waited_time = moment() - player.join_time
if waited_time >= 30000
ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE)
player.arena_quit_free = true
else if waited_time >= 5000 and waited_time < 6000
ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE)
return
, 1000
if settings.modules.heartbeat_detection.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and (room.hostinfo.time_limit == 0 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING) and !room.windbot
for player in room.get_playing_player() when player and (room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING or player.selected_preduel)
CLIENT_heartbeat_register(player, true)
return
, settings.modules.heartbeat_detection.interval
setInterval ()->
current_time = moment()
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.auto_death and !room.auto_death_triggered and current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death
room.auto_death_triggered = true
room.start_death()
, 1000
# spawn windbot # spawn windbot
windbot_looplimit = 0 windbot_looplimit = 0
windbot_process = global.windbot_process = null windbot_process = global.windbot_process = null
...@@ -3955,18 +3922,15 @@ spawn_windbot = global.spawn_windbot = () -> ...@@ -3955,18 +3922,15 @@ spawn_windbot = global.spawn_windbot = () ->
return return
return return
if settings.modules.windbot.enabled and settings.modules.windbot.spawn
spawn_windbot()
global.rebooted = false global.rebooted = false
#http #http
if settings.modules.http if true
addCallback = (callback, text)-> addCallback = (callback, text)->
if not callback then return text if not callback then return text
return callback + "( " + text + " );" return callback + "( " + text + " );"
requestListener = (request, response)-> httpRequestListener = (request, response)->
parseQueryString = true parseQueryString = true
u = url.parse(request.url, parseQueryString) u = url.parse(request.url, parseQueryString)
#pass_validated = u.query.pass == settings.modules.http.password #pass_validated = u.query.pass == settings.modules.http.password
...@@ -4008,14 +3972,14 @@ if settings.modules.http ...@@ -4008,14 +3972,14 @@ if settings.modules.http
) )
else if u.pathname == '/api/duellog' and settings.modules.tournament_mode.enabled else if u.pathname == '/api/duellog' and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "duel_log", "duel_log") if !await auth.auth(u.query.username, u.query.pass, "duel_log", "duel_log")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "[{name:'密码错误'}]")) response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"))
return return
else else
response.writeHead(200) response.writeHead(200)
duellog = JSON.stringify duel_log.duel_log, null, 2 duellog = JSON.stringify(await dataManager.getDuelLogJSON(settings.modules.tournament_mode), null, 2)
response.end(addCallback(u.query.callback, duellog)) response.end(addCallback(u.query.callback, duellog))
else if u.pathname == '/api/getkeys' and settings.modules.vip.enabled else if u.pathname == '/api/getkeys' and settings.modules.vip.enabled
...@@ -4034,7 +3998,7 @@ if settings.modules.http ...@@ -4034,7 +3998,7 @@ if settings.modules.http
ret_keys = ret_keys + u.query.keytype + "D" + settings.port + ":" + key + "\n" ret_keys = ret_keys + u.query.keytype + "D" + settings.port + ":" + key + "\n"
response.end(addCallback(u.query.callback, ret_keys)) response.end(addCallback(u.query.callback, ret_keys))
else if u.pathname == '/api/archive.zip' and settings.modules.tournament_mode.enabled else if u.pathname == '/api/archive.zip' and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay_archive") if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay_archive")
response.writeHead(403) response.writeHead(403)
response.end("Invalid password.") response.end("Invalid password.")
...@@ -4044,9 +4008,9 @@ if settings.modules.http ...@@ -4044,9 +4008,9 @@ if settings.modules.http
archive_name = moment().format('YYYY-MM-DD HH-mm-ss') + ".zip" archive_name = moment().format('YYYY-MM-DD HH-mm-ss') + ".zip"
archive_args = ["a", "-mx0", "-y", archive_name] archive_args = ["a", "-mx0", "-y", archive_name]
check = false check = false
for replay in duel_log.duel_log for filename in await dataManager.getAllReplayFilenames()
check = true check = true
archive_args.push(replay.replay_filename) archive_args.push(filename)
if !check if !check
response.writeHead(403) response.writeHead(403)
response.end("Duel logs not found.") response.end("Duel logs not found.")
...@@ -4077,7 +4041,7 @@ if settings.modules.http ...@@ -4077,7 +4041,7 @@ if settings.modules.http
response.writeHead(403) response.writeHead(403)
response.end("Failed reading replays. " + error) response.end("Failed reading replays. " + error)
else if u.pathname == '/api/clearlog' and settings.modules.tournament_mode.enabled else if u.pathname == '/api/clearlog' and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "clear_duel_log", "clear_duel_log") if !await auth.auth(u.query.username, u.query.pass, "clear_duel_log", "clear_duel_log")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "[{name:'密码错误'}]")) response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"))
...@@ -4085,15 +4049,14 @@ if settings.modules.http ...@@ -4085,15 +4049,14 @@ if settings.modules.http
else else
response.writeHead(200) response.writeHead(200)
if settings.modules.tournament_mode.log_save_path if settings.modules.tournament_mode.log_save_path
fs.writeFile(settings.modules.tournament_mode.log_save_path + 'duel_log.' + moment().format('YYYY-MM-DD HH-mm-ss') + '.json', JSON.stringify(duel_log, null, 2), (err) -> fs.writeFile(settings.modules.tournament_mode.log_save_path + 'duel_log.' + moment().format('YYYY-MM-DD HH-mm-ss') + '.json', JSON.stringify(await dataManager.getDuelLogJSON(settings.modules.tournament_mode), null, 2), (err) ->
if err if err
log.warn 'DUEL LOG SAVE ERROR', err log.warn 'DUEL LOG SAVE ERROR', err
) )
duel_log.duel_log = [] await dataManager.clearDuelLog()
setting_save(duel_log)
response.end(addCallback(u.query.callback, "[{name:'Success'}]")) response.end(addCallback(u.query.callback, "[{name:'Success'}]"))
else if _.startsWith(u.pathname, '/api/replay') and settings.modules.tournament_mode.enabled else if _.startsWith(u.pathname, '/api/replay') and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay") if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay")
response.writeHead(403) response.writeHead(403)
response.end("密码错误") response.end("密码错误")
...@@ -4144,7 +4107,7 @@ if settings.modules.http ...@@ -4144,7 +4107,7 @@ if settings.modules.http
u.query.stop = false u.query.stop = false
response.writeHead(200) response.writeHead(200)
try try
await util.promisify(setting_change)(settings, 'modules:stop', u.query.stop) await setting_change(settings, 'modules:stop', u.query.stop)
response.end(addCallback(u.query.callback, "['stop ok', '" + u.query.stop + "']")) response.end(addCallback(u.query.callback, "['stop ok', '" + u.query.stop + "']"))
catch err catch err
response.end(addCallback(u.query.callback, "['stop fail', '" + u.query.stop + "']")) response.end(addCallback(u.query.callback, "['stop fail', '" + u.query.stop + "']"))
...@@ -4155,7 +4118,7 @@ if settings.modules.http ...@@ -4155,7 +4118,7 @@ if settings.modules.http
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
try try
await util.promisify(setting_change)(settings, 'modules:welcome', u.query.welcome) await setting_change(settings, 'modules:welcome', u.query.welcome)
response.end(addCallback(u.query.callback, "['welcome ok', '" + u.query.welcome + "']")) response.end(addCallback(u.query.callback, "['welcome ok', '" + u.query.welcome + "']"))
catch err catch err
response.end(addCallback(u.query.callback, "['welcome fail', '" + u.query.welcome + "']")) response.end(addCallback(u.query.callback, "['welcome fail', '" + u.query.welcome + "']"))
...@@ -4173,36 +4136,26 @@ if settings.modules.http ...@@ -4173,36 +4136,26 @@ if settings.modules.http
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
tasks = { success = await load_tips()
tips: load_tips
}
if settings.modules.tips.get_zh
tasks.tips_zh = load_tips_zh
_async.auto(tasks, (err)->
response.writeHead(200) response.writeHead(200)
if(err) if success
response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"))
else
response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']")) response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']"))
) else
response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"))
else if u.query.loaddialogues else if u.query.loaddialogues
if !await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues") if !await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
tasks = { success = await load_dialogues()
dialogues: load_dialogues
}
if settings.modules.dialogues.get_custom if settings.modules.dialogues.get_custom
tasks.dialogues_custom = load_dialogues_custom success = await load_dialogues_custom() and success
_async.auto(tasks, (err)->
response.writeHead(200) response.writeHead(200)
if(err) if success
response.end(addCallback(u.query.callback, "['dialogues fail', '" + settings.modules.dialogues.get + "']")) response.end(addCallback(u.query.callback, "['dialogue ok', '" + settings.modules.tips.get + "']"))
else else
response.end(addCallback(u.query.callback, "['dialogues ok', '" +settings.modules.dialogues.get + "']")) response.end(addCallback(u.query.callback, "['dialogue fail', '" + settings.modules.tips.get + "']"))
)
else if u.query.ban else if u.query.ban
if !await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user") if !await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user")
...@@ -4311,24 +4264,4 @@ if settings.modules.http ...@@ -4311,24 +4264,4 @@ if settings.modules.http
response.end() response.end()
return return
http_server = http.createServer(requestListener) init()
http_server.listen settings.modules.http.port
if settings.modules.http.ssl.enabled
https = require 'https'
options =
cert: fs.readFileSync(settings.modules.http.ssl.cert)
key: fs.readFileSync(settings.modules.http.ssl.key)
https_server = https.createServer(options, requestListener)
if settings.modules.http.websocket_roomlist and roomlist
roomlist.init https_server, ROOM_all
https_server.listen settings.modules.http.ssl.port
if not fs.existsSync('./plugins')
fs.mkdirSync('./plugins')
plugin_list = fs.readdirSync("./plugins")
for plugin_filename in plugin_list
plugin_path = process.cwd() + "/plugins/" + plugin_filename
require(plugin_path)
log.info("Plugin loaded:", plugin_filename)
This source diff could not be displayed because it is too large. You can view the blob instead.
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