Commit d7bf04cf authored by nanahira's avatar nanahira

Merge branch 'develop'

parents 4c6a0635 5d2a13ec
lzma = require 'lzma'
fs = require 'fs'
# Deck = require './Deck.js'
class replayHeader
@replayCompressedFlag = 0x1
@replayTagFlag = 0x2
@replayDecodedFlag = 0x4
constructor: ->
@id = 0
@version = 0
@flag = 0
@seed = 0
@dataSizeRaw = []
@hash = 0
@props = []
getDataSize: ->
@dataSizeRaw[0] + @dataSizeRaw[1] * 0x100 + @dataSizeRaw[2] * 0x10000 + @dataSizeRaw[3] * 0x1000000
getIsTag: ->
@flag & replayHeader.replayTagFlag > 0
getIsCompressed: ->
@flag & replayHeader.replayCompressedFlag > 0
getLzmaHeader: ->
bytes = [].concat(@props[0..4], @dataSizeRaw, [0, 0, 0, 0])
Buffer.from(bytes)
Object.defineProperty replayHeader.prototype, 'dataSize', get: @getDataSize
Object.defineProperty replayHeader.prototype, 'isTag', get: @getIsTag
Object.defineProperty replayHeader.prototype, 'isCompressed', get: @getIsCompressed
class ReplayReader
constructor: (buffer) ->
@pointer = 0
@buffer = buffer
readByte: ->
answer = @buffer.readUInt8(@pointer)
@pointer += 1
answer
readByteArray: (length) ->
answer = []
answer.push @readByte() for i in [1..length]
answer
readInt8: ->
answer = @buffer.readInt8(@pointer)
@pointer += 1
answer
readUInt8: ->
answer = @buffer.readUInt8(@pointer)
@pointer += 1
answer
readInt16: ->
answer = @buffer.readInt16LE @pointer
@pointer += 2
answer
readInt32: ->
answer = @buffer.readInt32LE @pointer
@pointer += 4
answer
readAll: ->
answer = @buffer.slice(@pointer)
# @pointer = 0
answer
readString: (length) ->
if @pointer + length > @buffer.length
return null
full = @buffer.slice(@pointer, @pointer + length).toString('utf-16le')
answer = full.split("\u0000")[0]
@pointer += length
answer
readRaw: (length) ->
if @pointer + length > @buffer.length
return null
answer = @buffer.slice(@pointer, @pointer + length)
@pointer += length
answer
class Replay
constructor: ->
@header = null
@hostName = ""
@clientName = ""
@startLp = 0
@startHand = 0
@drawCount = 0
@opt = 0
@hostDeck = null
@clientDeck = null
@tagHostName = null
@tagClientName = null
@tagHostDeck = null
@tagClientDeck = null
@responses = null
getDecks: ->
if @isTag
[@hostDeck, @clientDeck, @tagHostDeck, @tagClientDeck]
else
[@hostDeck, @clientDeck]
getIsTag: ->
@header == null ? false : @header.isTag
@fromFile: (filePath) ->
Replay.fromBuffer await fs.promises.readFile filePath
@fromBuffer: (buffer) ->
reader = new ReplayReader buffer
header = Replay.readHeader reader
restBuffer = reader.readAll()
lzmaBuffer = Buffer.concat [header.getLzmaHeader(), restBuffer]
if header.isCompressed
decompressed = restBuffer
else
decompressed = Buffer.from lzma.decompress lzmaBuffer
reader = new ReplayReader decompressed
replay = Replay.readReplay header, reader
replay
@readHeader: (reader) ->
header = new replayHeader()
header.id = reader.readInt32()
header.version = reader.readInt32()
header.flag = reader.readInt32()
header.seed = reader.readInt32()
header.dataSizeRaw = reader.readByteArray 4
header.hash = reader.readInt32()
header.props = reader.readByteArray 8
header
@readReplay: (header, reader) ->
replay = new Replay()
replay.header = header
replay.hostName = reader.readString(40)
replay.tagHostName = reader.readString(40) if header.isTag
replay.tagClientName = reader.readString(40) if header.isTag
replay.clientName = reader.readString(40)
replay.startLp = reader.readInt32()
replay.startHand = reader.readInt32()
replay.drawCount = reader.readInt32()
replay.opt = reader.readInt32()
replay.hostDeck = Replay.readDeck reader
replay.tagHostDeck = Replay.readDeck reader if header.isTag
replay.tagClientDeck = Replay.readDeck reader if header.isTag
replay.clientDeck = Replay.readDeck reader
replay.responses = Replay.readResponses reader
replay
@readDeck: (reader) ->
deck = {}
deck.main = Replay.readDeckPack reader
deck.ex = Replay.readDeckPack reader
deck
@readDeckPack: (reader) ->
length = reader.readInt32()
answer = []
answer.push reader.readInt32() for i in [1..length]
answer
@readResponses: (reader) ->
answer = []
while true
try
length = reader.readUInt8()
if length > 64
length = 64
single = reader.readRaw(length)
if !single
break
answer.push(single)
catch
break
answer
Object.defineProperty replayHeader.prototype, 'decks', get: @getDecks
Object.defineProperty replayHeader.prototype, 'isTag', get: @getIsTag
module.exports = Replay
// Generated by CoffeeScript 2.7.0
(function() {
var Replay, ReplayReader, fs, lzma, replayHeader;
lzma = require('lzma');
fs = require('fs');
replayHeader = (function() {
// Deck = require './Deck.js'
class replayHeader {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Replay = exports.ReplayHeader = exports.REPLAY_ID_YRP2 = exports.REPLAY_ID_YRP1 = exports.SEED_COUNT = void 0;
const fs = __importStar(require("fs"));
const lzma = __importStar(require("lzma"));
exports.SEED_COUNT = 8;
exports.REPLAY_ID_YRP1 = 0x31707279;
exports.REPLAY_ID_YRP2 = 0x32707279;
/**
* Metadata stored at the beginning of every replay file.
*/
class ReplayHeader {
constructor() {
this.id = 0;
this.version = 0;
......@@ -17,136 +51,154 @@
this.dataSizeRaw = [];
this.hash = 0;
this.props = [];
this.seedSequence = [];
this.headerVersion = 0;
this.value1 = 0;
this.value2 = 0;
this.value3 = 0;
}
getDataSize() {
return this.dataSizeRaw[0] + this.dataSizeRaw[1] * 0x100 + this.dataSizeRaw[2] * 0x10000 + this.dataSizeRaw[3] * 0x1000000;
/** Decompressed size as little‑endian 32‑bit */
get dataSize() {
return Buffer.from(this.dataSizeRaw).readUInt32LE(0);
}
getIsTag() {
return this.flag & replayHeader.replayTagFlag > 0;
get isTag() {
return (this.flag & ReplayHeader.REPLAY_TAG_FLAG) !== 0;
}
getIsCompressed() {
return this.flag & replayHeader.replayCompressedFlag > 0;
get isCompressed() {
return (this.flag & ReplayHeader.REPLAY_COMPRESSED_FLAG) !== 0;
}
/** Compose a valid 13‑byte LZMA header for this replay */
getLzmaHeader() {
var bytes;
bytes = [].concat(this.props.slice(0, 5), this.dataSizeRaw, [0, 0, 0, 0]);
const bytes = [
...this.props.slice(0, 5),
...this.dataSizeRaw,
0,
0,
0,
0,
];
return Buffer.from(bytes);
}
};
replayHeader.replayCompressedFlag = 0x1;
replayHeader.replayTagFlag = 0x2;
replayHeader.replayDecodedFlag = 0x4;
Object.defineProperty(replayHeader.prototype, 'dataSize', {
get: replayHeader.getDataSize
});
Object.defineProperty(replayHeader.prototype, 'isTag', {
get: replayHeader.getIsTag
});
Object.defineProperty(replayHeader.prototype, 'isCompressed', {
get: replayHeader.getIsCompressed
});
return replayHeader;
}).call(this);
ReplayReader = class ReplayReader {
}
exports.ReplayHeader = ReplayHeader;
ReplayHeader.REPLAY_COMPRESSED_FLAG = 0x1;
ReplayHeader.REPLAY_TAG_FLAG = 0x2;
ReplayHeader.REPLAY_DECODED_FLAG = 0x4;
ReplayHeader.REPLAY_SINGLE_MODE = 0x8;
ReplayHeader.REPLAY_UNIFORM = 0x10;
/** Utility for reading little‑endian primitives from a Buffer */
class ReplayReader {
constructor(buffer) {
this.pointer = 0;
this.buffer = buffer;
this.pointer = 0;
}
advance(size, read) {
const value = read();
this.pointer += size;
return value;
}
readByte() {
var answer;
answer = this.buffer.readUInt8(this.pointer);
this.pointer += 1;
return answer;
return this.advance(1, () => this.buffer.readUInt8(this.pointer));
}
readByteArray(length) {
var answer, i, j, ref;
answer = [];
for (i = j = 1, ref = length; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) {
answer.push(this.readByte());
}
return answer;
const out = [];
for (let i = 0; i < length; i++)
out.push(this.readByte());
return out;
}
readInt8() {
var answer;
answer = this.buffer.readInt8(this.pointer);
this.pointer += 1;
return answer;
return this.advance(1, () => this.buffer.readInt8(this.pointer));
}
readUInt8() {
var answer;
answer = this.buffer.readUInt8(this.pointer);
this.pointer += 1;
return answer;
return this.advance(1, () => this.buffer.readUInt8(this.pointer));
}
readInt16() {
var answer;
answer = this.buffer.readInt16LE(this.pointer);
this.pointer += 2;
return answer;
return this.advance(2, () => this.buffer.readInt16LE(this.pointer));
}
readInt32() {
var answer;
answer = this.buffer.readInt32LE(this.pointer);
this.pointer += 4;
return answer;
return this.advance(4, () => this.buffer.readInt32LE(this.pointer));
}
readUInt16() {
return this.advance(2, () => this.buffer.readUInt16LE(this.pointer));
}
readUInt32() {
return this.advance(4, () => this.buffer.readUInt32LE(this.pointer));
}
readAll() {
var answer;
answer = this.buffer.slice(this.pointer);
// @pointer = 0
return answer;
return this.buffer.slice(this.pointer);
}
readString(length) {
var answer, full;
if (this.pointer + length > this.buffer.length) {
if (this.pointer + length > this.buffer.length)
return null;
}
full = this.buffer.slice(this.pointer, this.pointer + length).toString('utf-16le');
answer = full.split("\u0000")[0];
const raw = this.buffer
.slice(this.pointer, this.pointer + length)
.toString('utf16le');
this.pointer += length;
return answer;
return raw.split('\u0000')[0];
}
readRaw(length) {
var answer;
if (this.pointer + length > this.buffer.length) {
if (this.pointer + length > this.buffer.length)
return null;
}
answer = this.buffer.slice(this.pointer, this.pointer + length);
const buf = this.buffer.slice(this.pointer, this.pointer + length);
this.pointer += length;
return answer;
return buf;
}
};
Replay = (function() {
class Replay {
}
/** Utility for writing little‑endian primitives into a Buffer */
class ReplayWriter {
constructor(buffer) {
this.buffer = buffer;
this.pointer = 0;
}
advance(action, size) {
action();
this.pointer += size;
}
writeByte(val) {
this.advance(() => this.buffer.writeUInt8(val, this.pointer), 1);
}
writeByteArray(values) {
for (const v of values)
this.writeByte(v);
}
writeInt8(val) {
this.advance(() => this.buffer.writeInt8(val, this.pointer), 1);
}
writeUInt8(val) {
this.advance(() => this.buffer.writeUInt8(val, this.pointer), 1);
}
writeInt16(val) {
this.advance(() => this.buffer.writeInt16LE(val, this.pointer), 2);
}
writeInt32(val) {
this.advance(() => this.buffer.writeInt32LE(val, this.pointer), 4);
}
writeUInt16(val) {
this.advance(() => this.buffer.writeUInt16LE(val, this.pointer), 2);
}
writeUInt32(val) {
this.advance(() => this.buffer.writeUInt32LE(val, this.pointer), 4);
}
writeAll(buf) {
this.buffer = Buffer.concat([this.buffer, buf]);
}
writeString(val, length) {
const raw = Buffer.from(val ?? '', 'utf16le');
const bytes = [...raw];
if (length !== undefined) {
const padding = new Array(Math.max(length - bytes.length, 0)).fill(0);
this.writeByteArray([...bytes, ...padding]);
}
else {
this.writeByteArray(bytes);
}
}
}
class Replay {
constructor() {
this.header = null;
this.hostName = "";
this.clientName = "";
this.hostName = '';
this.clientName = '';
this.startLp = 0;
this.startHand = 0;
this.drawCount = 0;
......@@ -157,138 +209,197 @@
this.tagClientName = null;
this.tagHostDeck = null;
this.tagClientDeck = null;
this.responses = null;
}
getDecks() {
if (this.isTag) {
return [this.hostDeck, this.clientDeck, this.tagHostDeck, this.tagClientDeck];
} else {
return [this.hostDeck, this.clientDeck];
}
}
getIsTag() {
var ref;
return (ref = this.header === null) != null ? ref : {
false: this.header.isTag
};
}
static async fromFile(filePath) {
return Replay.fromBuffer((await fs.promises.readFile(filePath)));
this.responses = [];
}
/** All deck objects in play order */
get decks() {
return this.isTag
? [
this.hostDeck,
this.clientDeck,
this.tagHostDeck,
this.tagClientDeck,
]
: [this.hostDeck, this.clientDeck];
}
get isTag() {
return this.header?.isTag ?? false;
}
/* ------------------ Static helpers ------------------ */
static async fromFile(path) {
return Replay.fromBuffer(await fs.promises.readFile(path));
}
static fromBuffer(buffer) {
var decompressed, header, lzmaBuffer, reader, replay, restBuffer;
reader = new ReplayReader(buffer);
header = Replay.readHeader(reader);
restBuffer = reader.readAll();
lzmaBuffer = Buffer.concat([header.getLzmaHeader(), restBuffer]);
if (header.isCompressed) {
decompressed = restBuffer;
} else {
decompressed = Buffer.from(lzma.decompress(lzmaBuffer));
}
reader = new ReplayReader(decompressed);
replay = Replay.readReplay(header, reader);
return replay;
}
const headerReader = new ReplayReader(buffer);
const header = Replay.readHeader(headerReader);
const raw = headerReader.readAll();
const body = header.isCompressed
? Replay.decompressBody(header, raw)
: raw;
const bodyReader = new ReplayReader(body);
return Replay.readReplay(header, bodyReader);
}
static decompressBody(header, raw) {
const lzmaBuffer = Buffer.concat([header.getLzmaHeader(), raw]);
// lzma‑native provides synchronous helpers.
return Buffer.from(lzma.decompress(lzmaBuffer));
}
static readHeader(reader) {
var header;
header = new replayHeader();
header.id = reader.readInt32();
header.version = reader.readInt32();
header.flag = reader.readInt32();
header.seed = reader.readInt32();
header.dataSizeRaw = reader.readByteArray(4);
header.hash = reader.readInt32();
header.props = reader.readByteArray(8);
return header;
}
static readReplay(header, reader) {
var replay;
replay = new Replay();
replay.header = header;
replay.hostName = reader.readString(40);
if (header.isTag) {
replay.tagHostName = reader.readString(40);
const h = new ReplayHeader();
h.id = reader.readUInt32();
h.version = reader.readUInt32();
h.flag = reader.readUInt32();
h.seed = reader.readUInt32();
h.dataSizeRaw = reader.readByteArray(4);
h.hash = reader.readUInt32();
h.props = reader.readByteArray(8);
if (h.id === exports.REPLAY_ID_YRP2) {
for (let i = 0; i < exports.SEED_COUNT; i++) {
h.seedSequence.push(reader.readUInt32());
}
h.headerVersion = reader.readUInt32();
h.value1 = reader.readUInt32();
h.value2 = reader.readUInt32();
h.value3 = reader.readUInt32();
}
return h;
}
static readReplay(header, reader) {
const r = new Replay();
r.header = header;
r.hostName = reader.readString(40) ?? '';
if (header.isTag) {
replay.tagClientName = reader.readString(40);
}
replay.clientName = reader.readString(40);
replay.startLp = reader.readInt32();
replay.startHand = reader.readInt32();
replay.drawCount = reader.readInt32();
replay.opt = reader.readInt32();
replay.hostDeck = Replay.readDeck(reader);
if (header.isTag) {
replay.tagHostDeck = Replay.readDeck(reader);
}
r.tagHostName = reader.readString(40);
r.tagClientName = reader.readString(40);
}
r.clientName = reader.readString(40) ?? '';
r.startLp = reader.readInt32();
r.startHand = reader.readInt32();
r.drawCount = reader.readInt32();
r.opt = reader.readInt32();
r.hostDeck = Replay.readDeck(reader);
if (header.isTag) {
replay.tagClientDeck = Replay.readDeck(reader);
r.tagHostDeck = Replay.readDeck(reader);
r.tagClientDeck = Replay.readDeck(reader);
}
replay.clientDeck = Replay.readDeck(reader);
replay.responses = Replay.readResponses(reader);
return replay;
r.clientDeck = Replay.readDeck(reader);
r.responses = Replay.readResponses(reader);
return r;
}
/* ------------------ Deck helpers ------------------ */
static readDeck(reader) {
var deck;
deck = {};
deck.main = Replay.readDeckPack(reader);
deck.ex = Replay.readDeckPack(reader);
return deck;
return {
main: Replay.readDeckPack(reader),
ex: Replay.readDeckPack(reader),
};
}
static readDeckPack(reader) {
var answer, i, j, length, ref;
length = reader.readInt32();
answer = [];
for (i = j = 1, ref = length; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) {
answer.push(reader.readInt32());
}
return answer;
const length = reader.readInt32();
const cards = [];
for (let i = 0; i < length; i++)
cards.push(reader.readInt32());
return cards;
}
/* ------------------ Response helpers ------------------ */
static readResponses(reader) {
var answer, length, single;
answer = [];
const out = [];
while (true) {
try {
length = reader.readUInt8();
if (length > 64) {
let length = reader.readUInt8();
if (length > 64)
length = 64;
}
single = reader.readRaw(length);
if (!single) {
const segment = reader.readRaw(length);
if (!segment)
break;
out.push(segment);
}
answer.push(single);
} catch (error) {
catch {
break;
}
}
return answer;
}
};
Object.defineProperty(replayHeader.prototype, 'decks', {
get: Replay.getDecks
});
Object.defineProperty(replayHeader.prototype, 'isTag', {
get: Replay.getIsTag
});
return Replay;
}).call(this);
module.exports = Replay;
}).call(this);
return out;
}
/* ------------------ Writing ------------------ */
toBuffer() {
if (!this.header)
throw new Error('Header not initialised');
const headerWriter = new ReplayWriter(Buffer.alloc(32));
this.writeHeader(headerWriter);
const deckSize = (d) => ((d?.main.length ?? 0) + (d?.ex.length ?? 0)) * 4 + 8;
const responseSize = this.responses.reduce((s, b) => s + b.length + 1, 0);
let contentSize = 96 + deckSize(this.hostDeck) + deckSize(this.clientDeck) + responseSize;
if (this.header.isTag) {
contentSize +=
deckSize(this.tagHostDeck) + deckSize(this.tagClientDeck) + 80;
}
const contentWriter = new ReplayWriter(Buffer.alloc(contentSize));
this.writeContent(contentWriter);
let body = contentWriter.buffer;
if (this.header.isCompressed) {
body = Buffer.from(lzma.compress(body));
body = body.slice(13); // strip header like original implementation
}
return Buffer.concat([headerWriter.buffer, body]);
}
async writeToFile(path) {
await fs.promises.writeFile(path, this.toBuffer());
}
writeHeader(w) {
w.writeUInt32(this.header.id);
w.writeUInt32(this.header.version);
w.writeUInt32(this.header.flag);
w.writeUInt32(this.header.seed);
w.writeByteArray(this.header.dataSizeRaw);
w.writeUInt32(this.header.hash);
w.writeByteArray(this.header.props);
if (this.header.id === exports.REPLAY_ID_YRP2) {
for (let i = 0; i < exports.SEED_COUNT; i++) {
w.writeUInt32(this.header.seedSequence[i]);
}
w.writeUInt32(this.header.headerVersion);
w.writeUInt32(this.header.value1);
w.writeUInt32(this.header.value2);
w.writeUInt32(this.header.value3);
}
}
writeContent(w) {
w.writeString(this.hostName, 40);
if (this.header.isTag) {
w.writeString(this.tagHostName, 40);
w.writeString(this.tagClientName, 40);
}
w.writeString(this.clientName, 40);
w.writeInt32(this.startLp);
w.writeInt32(this.startHand);
w.writeInt32(this.drawCount);
w.writeInt32(this.opt);
Replay.writeDeck(w, this.hostDeck);
if (this.header.isTag) {
Replay.writeDeck(w, this.tagHostDeck);
Replay.writeDeck(w, this.tagClientDeck);
}
Replay.writeDeck(w, this.clientDeck);
Replay.writeResponses(w, this.responses);
}
static writeDeck(w, d) {
if (!d) {
w.writeInt32(0);
w.writeInt32(0);
return;
}
Replay.writeDeckPack(w, d.main);
Replay.writeDeckPack(w, d.ex);
}
static writeDeckPack(w, pack) {
w.writeInt32(pack.length);
for (const card of pack)
w.writeInt32(card);
}
static writeResponses(w, res) {
for (const buf of res) {
w.writeUInt8(buf.length);
w.writeByteArray(buf);
}
}
}
exports.Replay = Replay;
import * as fs from 'fs';
import * as lzma from 'lzma';
/** A minimal deck representation */
export interface DeckObject {
main: number[];
ex: number[];
}
export const SEED_COUNT = 8;
export const REPLAY_ID_YRP1 = 0x31707279;
export const REPLAY_ID_YRP2 = 0x32707279;
/**
* Metadata stored at the beginning of every replay file.
*/
export class ReplayHeader {
static readonly REPLAY_COMPRESSED_FLAG = 0x1;
static readonly REPLAY_TAG_FLAG = 0x2;
static readonly REPLAY_DECODED_FLAG = 0x4;
static readonly REPLAY_SINGLE_MODE = 0x8;
static readonly REPLAY_UNIFORM = 0x10;
id = 0;
version = 0;
flag = 0;
seed = 0;
dataSizeRaw: number[] = [];
hash = 0;
props: number[] = [];
seedSequence: number[] = [];
headerVersion = 0;
value1 = 0;
value2 = 0;
value3 = 0;
/** Decompressed size as little‑endian 32‑bit */
get dataSize(): number {
return Buffer.from(this.dataSizeRaw).readUInt32LE(0);
}
get isTag(): boolean {
return (this.flag & ReplayHeader.REPLAY_TAG_FLAG) !== 0;
}
get isCompressed(): boolean {
return (this.flag & ReplayHeader.REPLAY_COMPRESSED_FLAG) !== 0;
}
/** Compose a valid 13‑byte LZMA header for this replay */
getLzmaHeader(): Buffer {
const bytes = [
...this.props.slice(0, 5),
...this.dataSizeRaw,
0,
0,
0,
0,
];
return Buffer.from(bytes);
}
}
/** Utility for reading little‑endian primitives from a Buffer */
class ReplayReader {
private pointer = 0;
constructor(private readonly buffer: Buffer) {}
private advance<T>(size: number, read: () => T): T {
const value = read();
this.pointer += size;
return value;
}
readByte(): number {
return this.advance(1, () => this.buffer.readUInt8(this.pointer));
}
readByteArray(length: number): number[] {
const out: number[] = [];
for (let i = 0; i < length; i++) out.push(this.readByte());
return out;
}
readInt8(): number {
return this.advance(1, () => this.buffer.readInt8(this.pointer));
}
readUInt8(): number {
return this.advance(1, () => this.buffer.readUInt8(this.pointer));
}
readInt16(): number {
return this.advance(2, () => this.buffer.readInt16LE(this.pointer));
}
readInt32(): number {
return this.advance(4, () => this.buffer.readInt32LE(this.pointer));
}
readUInt16(): number {
return this.advance(2, () => this.buffer.readUInt16LE(this.pointer));
}
readUInt32(): number {
return this.advance(4, () => this.buffer.readUInt32LE(this.pointer));
}
readAll(): Buffer {
return this.buffer.slice(this.pointer);
}
readString(length: number): string | null {
if (this.pointer + length > this.buffer.length) return null;
const raw = this.buffer
.slice(this.pointer, this.pointer + length)
.toString('utf16le');
this.pointer += length;
return raw.split('\u0000')[0];
}
readRaw(length: number): Buffer | null {
if (this.pointer + length > this.buffer.length) return null;
const buf = this.buffer.slice(this.pointer, this.pointer + length);
this.pointer += length;
return buf;
}
}
/** Utility for writing little‑endian primitives into a Buffer */
class ReplayWriter {
private pointer = 0;
constructor(public buffer: Buffer) {}
private advance(action: () => void, size: number): void {
action();
this.pointer += size;
}
writeByte(val: number): void {
this.advance(() => this.buffer.writeUInt8(val, this.pointer), 1);
}
writeByteArray(values: Iterable<number>): void {
for (const v of values) this.writeByte(v);
}
writeInt8(val: number): void {
this.advance(() => this.buffer.writeInt8(val, this.pointer), 1);
}
writeUInt8(val: number): void {
this.advance(() => this.buffer.writeUInt8(val, this.pointer), 1);
}
writeInt16(val: number): void {
this.advance(() => this.buffer.writeInt16LE(val, this.pointer), 2);
}
writeInt32(val: number): void {
this.advance(() => this.buffer.writeInt32LE(val, this.pointer), 4);
}
writeUInt16(val: number): void {
this.advance(() => this.buffer.writeUInt16LE(val, this.pointer), 2);
}
writeUInt32(val: number): void {
this.advance(() => this.buffer.writeUInt32LE(val, this.pointer), 4);
}
writeAll(buf: Buffer): void {
this.buffer = Buffer.concat([this.buffer, buf]);
}
writeString(val: string | null, length?: number): void {
const raw = Buffer.from(val ?? '', 'utf16le');
const bytes = [...raw];
if (length !== undefined) {
const padding = new Array(Math.max(length - bytes.length, 0)).fill(0);
this.writeByteArray([...bytes, ...padding]);
} else {
this.writeByteArray(bytes);
}
}
}
export class Replay {
header: ReplayHeader | null = null;
hostName = '';
clientName = '';
startLp = 0;
startHand = 0;
drawCount = 0;
opt = 0;
hostDeck: DeckObject | null = null;
clientDeck: DeckObject | null = null;
tagHostName: string | null = null;
tagClientName: string | null = null;
tagHostDeck: DeckObject | null = null;
tagClientDeck: DeckObject | null = null;
responses: Buffer[] = [];
/** All deck objects in play order */
get decks(): DeckObject[] {
return this.isTag
? [
this.hostDeck!,
this.clientDeck!,
this.tagHostDeck!,
this.tagClientDeck!,
]
: [this.hostDeck!, this.clientDeck!];
}
get isTag(): boolean {
return this.header?.isTag ?? false;
}
/* ------------------ Static helpers ------------------ */
static async fromFile(path: string): Promise<Replay> {
return Replay.fromBuffer(await fs.promises.readFile(path));
}
static fromBuffer(buffer: Buffer): Replay {
const headerReader = new ReplayReader(buffer);
const header = Replay.readHeader(headerReader);
const raw = headerReader.readAll();
const body = header.isCompressed
? Replay.decompressBody(header, raw)
: raw;
const bodyReader = new ReplayReader(body);
return Replay.readReplay(header, bodyReader);
}
private static decompressBody(header: ReplayHeader, raw: Buffer): Buffer {
const lzmaBuffer = Buffer.concat([header.getLzmaHeader(), raw]);
// lzma‑native provides synchronous helpers.
return Buffer.from(lzma.decompress(lzmaBuffer));
}
private static readHeader(reader: ReplayReader): ReplayHeader {
const h = new ReplayHeader();
h.id = reader.readUInt32();
h.version = reader.readUInt32();
h.flag = reader.readUInt32();
h.seed = reader.readUInt32();
h.dataSizeRaw = reader.readByteArray(4);
h.hash = reader.readUInt32();
h.props = reader.readByteArray(8);
if (h.id === REPLAY_ID_YRP2) {
for(let i = 0; i < SEED_COUNT; i++) {
h.seedSequence.push(reader.readUInt32());
}
h.headerVersion = reader.readUInt32();
h.value1 = reader.readUInt32();
h.value2 = reader.readUInt32();
h.value3 = reader.readUInt32();
}
return h;
}
private static readReplay(header: ReplayHeader, reader: ReplayReader): Replay {
const r = new Replay();
r.header = header;
r.hostName = reader.readString(40) ?? '';
if (header.isTag) {
r.tagHostName = reader.readString(40);
r.tagClientName = reader.readString(40);
}
r.clientName = reader.readString(40) ?? '';
r.startLp = reader.readInt32();
r.startHand = reader.readInt32();
r.drawCount = reader.readInt32();
r.opt = reader.readInt32();
r.hostDeck = Replay.readDeck(reader);
if (header.isTag) {
r.tagHostDeck = Replay.readDeck(reader);
r.tagClientDeck = Replay.readDeck(reader);
}
r.clientDeck = Replay.readDeck(reader);
r.responses = Replay.readResponses(reader);
return r;
}
/* ------------------ Deck helpers ------------------ */
private static readDeck(reader: ReplayReader): DeckObject {
return {
main: Replay.readDeckPack(reader),
ex: Replay.readDeckPack(reader),
};
}
private static readDeckPack(reader: ReplayReader): number[] {
const length = reader.readInt32();
const cards: number[] = [];
for (let i = 0; i < length; i++) cards.push(reader.readInt32());
return cards;
}
/* ------------------ Response helpers ------------------ */
private static readResponses(reader: ReplayReader): Buffer[] {
const out: Buffer[] = [];
while (true) {
try {
let length = reader.readUInt8();
if (length > 64) length = 64;
const segment = reader.readRaw(length);
if (!segment) break;
out.push(segment);
} catch {
break;
}
}
return out;
}
/* ------------------ Writing ------------------ */
toBuffer(): Buffer {
if (!this.header) throw new Error('Header not initialised');
const headerWriter = new ReplayWriter(Buffer.alloc(32));
this.writeHeader(headerWriter);
const deckSize = (d: DeckObject | null) =>
((d?.main.length ?? 0) + (d?.ex.length ?? 0)) * 4 + 8;
const responseSize = this.responses.reduce((s, b) => s + b.length + 1, 0);
let contentSize =
96 + deckSize(this.hostDeck) + deckSize(this.clientDeck) + responseSize;
if (this.header.isTag) {
contentSize +=
deckSize(this.tagHostDeck) + deckSize(this.tagClientDeck) + 80;
}
const contentWriter = new ReplayWriter(Buffer.alloc(contentSize));
this.writeContent(contentWriter);
let body = contentWriter.buffer;
if (this.header.isCompressed) {
body = Buffer.from(lzma.compress(body));
body = body.slice(13); // strip header like original implementation
}
return Buffer.concat([headerWriter.buffer, body]);
}
async writeToFile(path: string): Promise<void> {
await fs.promises.writeFile(path, this.toBuffer());
}
private writeHeader(w: ReplayWriter): void {
w.writeUInt32(this.header!.id);
w.writeUInt32(this.header!.version);
w.writeUInt32(this.header!.flag);
w.writeUInt32(this.header!.seed);
w.writeByteArray(this.header!.dataSizeRaw);
w.writeUInt32(this.header!.hash);
w.writeByteArray(this.header!.props);
if (this.header!.id === REPLAY_ID_YRP2) {
for (let i = 0; i < SEED_COUNT; i++) {
w.writeUInt32(this.header!.seedSequence[i]);
}
w.writeUInt32(this.header!.headerVersion);
w.writeUInt32(this.header!.value1);
w.writeUInt32(this.header!.value2);
w.writeUInt32(this.header!.value3);
}
}
private writeContent(w: ReplayWriter): void {
w.writeString(this.hostName, 40);
if (this.header!.isTag) {
w.writeString(this.tagHostName, 40);
w.writeString(this.tagClientName, 40);
}
w.writeString(this.clientName, 40);
w.writeInt32(this.startLp);
w.writeInt32(this.startHand);
w.writeInt32(this.drawCount);
w.writeInt32(this.opt);
Replay.writeDeck(w, this.hostDeck);
if (this.header!.isTag) {
Replay.writeDeck(w, this.tagHostDeck);
Replay.writeDeck(w, this.tagClientDeck);
}
Replay.writeDeck(w, this.clientDeck);
Replay.writeResponses(w, this.responses);
}
private static writeDeck(w: ReplayWriter, d: DeckObject | null): void {
if (!d) {
w.writeInt32(0);
w.writeInt32(0);
return;
}
Replay.writeDeckPack(w, d.main);
Replay.writeDeckPack(w, d.ex);
}
private static writeDeckPack(w: ReplayWriter, pack: number[]): void {
w.writeInt32(pack.length);
for (const card of pack) w.writeInt32(card);
}
private static writeResponses(w: ReplayWriter, res: Buffer[]): void {
for (const buf of res) {
w.writeUInt8(buf.length);
w.writeByteArray(buf);
}
}
}
......@@ -17,7 +17,7 @@ class Handler {
}
async handle(buffer, info, datas, params) {
if (this.synchronous) {
return !!(await this.handler(buffer, info, datas, params));
return await this.handler(buffer, info, datas, params);
}
else {
const newBuffer = Buffer.from(buffer);
......@@ -183,7 +183,7 @@ class YGOProMessagesHelper {
let messageLength = 0;
let bufferProto = 0;
let datas = [];
const limit = preconnect ? 2 : this.singleHandleLimit;
const limit = preconnect ? protoFilter.length * 3 : this.singleHandleLimit;
for (let l = 0; l < limit; ++l) {
if (messageLength === 0) {
if (messageBuffer.length >= 2) {
......@@ -223,6 +223,7 @@ class YGOProMessagesHelper {
break;
}
let buffer = messageBuffer.slice(3, 2 + messageLength);
let bufferMutated = false;
//console.log(l, direction, proto, cancel);
for (let priority = 0; priority < 4; ++priority) {
if (cancel) {
......@@ -231,28 +232,53 @@ class YGOProMessagesHelper {
const handlerCollection = this.handlers[direction][priority];
if (proto && handlerCollection.has(bufferProto)) {
let struct = this.structs.get(this.proto_structs[direction][proto]);
for (const handler of handlerCollection.get(bufferProto)) {
let info = null;
if (struct) {
struct._setBuff(buffer);
info = underscore_1.default.clone(struct.fields);
}
for (let handler of handlerCollection.get(bufferProto)) {
cancel = await handler.handle(buffer, info, datas, params);
if (cancel) {
if (Buffer.isBuffer(cancel)) {
buffer = cancel;
bufferMutated = true;
cancel = false;
}
else if (typeof cancel === "string") {
if (cancel === '_cancel') {
return {
datas: [],
feedback
};
}
else if (cancel.startsWith('_shrink_')) {
const targetShrinkCount = parseInt(cancel.slice(8));
if (targetShrinkCount > buffer.length) {
cancel = true;
}
else {
buffer = buffer.slice(0, buffer.length - targetShrinkCount);
bufferMutated = true;
cancel = false;
}
}
}
break;
}
}
}
}
if (!cancel) {
if (bufferMutated) {
const newLength = buffer.length + 1;
messageBuffer.writeUInt16LE(newLength, 0);
datas.push(Buffer.concat([messageBuffer.slice(0, 3), buffer]));
}
else {
datas.push(messageBuffer.slice(0, 2 + messageLength));
}
}
messageBuffer = messageBuffer.slice(2 + messageLength);
messageLength = 0;
bufferProto = 0;
......@@ -281,4 +307,3 @@ class YGOProMessagesHelper {
}
}
exports.YGOProMessagesHelper = YGOProMessagesHelper;
//# sourceMappingURL=YGOProMessages.js.map
\ No newline at end of file
......@@ -8,15 +8,15 @@ import net from "net";
class Handler {
private handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string>;
private handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string | Buffer>;
synchronous: boolean;
constructor(handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string>, synchronous: boolean) {
constructor(handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean | string | Buffer>, synchronous: boolean) {
this.handler = handler;
this.synchronous = synchronous || false;
}
async handle(buffer: Buffer, info: any, datas: Buffer[], params: any): Promise<boolean | string> {
async handle(buffer: Buffer, info: any, datas: Buffer[], params: any): Promise<boolean | string | Buffer> {
if (this.synchronous) {
return !!(await this.handler(buffer, info, datas, params));
return await this.handler(buffer, info, datas, params);
} else {
const newBuffer = Buffer.from(buffer);
const newDatas = datas.map(b => Buffer.from(b));
......@@ -237,7 +237,7 @@ export class YGOProMessagesHelper {
let messageLength = 0;
let bufferProto = 0;
let datas: Buffer[] = [];
const limit = preconnect ? 2 : this.singleHandleLimit;
const limit = preconnect ? protoFilter.length * 3 : this.singleHandleLimit;
for (let l = 0; l < limit; ++l) {
if (messageLength === 0) {
if (messageBuffer.length >= 2) {
......@@ -264,7 +264,7 @@ export class YGOProMessagesHelper {
} else {
if (messageBuffer.length >= 2 + messageLength) {
const proto = this.constants[direction][bufferProto];
let cancel: string | boolean = proto && protoFilter && !protoFilter.includes(proto);
let cancel: string | boolean | Buffer = proto && protoFilter && !protoFilter.includes(proto);
if (cancel && preconnect) {
feedback = {
type: "INVALID_PACKET",
......@@ -273,6 +273,7 @@ export class YGOProMessagesHelper {
break;
}
let buffer = messageBuffer.slice(3, 2 + messageLength);
let bufferMutated = false;
//console.log(l, direction, proto, cancel);
for (let priority = 0; priority < 4; ++priority) {
if (cancel) {
......@@ -281,19 +282,34 @@ export class YGOProMessagesHelper {
const handlerCollection: Map<number, Handler[]> = this.handlers[direction][priority];
if (proto && handlerCollection.has(bufferProto)) {
let struct = this.structs.get(this.proto_structs[direction][proto]);
for (const handler of handlerCollection.get(bufferProto)) {
let info = null;
if (struct) {
struct._setBuff(buffer);
info = _.clone(struct.fields);
}
for (let handler of handlerCollection.get(bufferProto)) {
cancel = await handler.handle(buffer, info, datas, params);
if (cancel) {
if (Buffer.isBuffer(cancel)) {
buffer = cancel as any;
bufferMutated = true;
cancel = false;
} else if (typeof cancel === "string") {
if (cancel === '_cancel') {
return {
datas: [],
feedback
}
} else if (cancel.startsWith('_shrink_')) {
const targetShrinkCount = parseInt(cancel.slice(8));
if (targetShrinkCount > buffer.length) {
cancel = true;
} else {
buffer = buffer.slice(0, buffer.length - targetShrinkCount);
bufferMutated = true;
cancel = false;
}
}
}
break;
}
......@@ -301,8 +317,14 @@ export class YGOProMessagesHelper {
}
}
if (!cancel) {
if (bufferMutated) {
const newLength = buffer.length + 1;
messageBuffer.writeUInt16LE(newLength, 0);
datas.push(Buffer.concat([messageBuffer.slice(0, 3), buffer]));
} else {
datas.push(messageBuffer.slice(0, 2 + messageLength));
}
}
messageBuffer = messageBuffer.slice(2 + messageLength);
messageLength = 0;
bufferProto = 0;
......
"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.ClientVersionBlocker = void 0;
const aragami_1 = require("aragami");
let ClientVersionBlocker = class ClientVersionBlocker {
};
exports.ClientVersionBlocker = ClientVersionBlocker;
__decorate([
(0, aragami_1.CacheKey)(),
__metadata("design:type", String)
], ClientVersionBlocker.prototype, "clientKey", void 0);
exports.ClientVersionBlocker = ClientVersionBlocker = __decorate([
(0, aragami_1.CacheTTL)(60000)
], ClientVersionBlocker);
import { CacheKey, CacheTTL } from "aragami";
@CacheTTL(60000)
export class ClientVersionBlocker {
@CacheKey()
clientKey: string;
}
......@@ -54,4 +54,3 @@ class AthleticChecker {
}
}
exports.AthleticChecker = AthleticChecker;
//# sourceMappingURL=athletic-check.js.map
\ No newline at end of file
......@@ -96,4 +96,3 @@ class Challonge {
}
}
exports.Challonge = Challonge;
//# sourceMappingURL=challonge.js.map
\ No newline at end of file
......@@ -592,4 +592,3 @@ class DataManager {
}
}
exports.DataManager = DataManager;
//# sourceMappingURL=DataManager.js.map
\ No newline at end of file
......@@ -5,40 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeDeck = encodeDeck;
exports.decodeDeck = decodeDeck;
const assert_1 = __importDefault(require("assert"));
const ygopro_deck_encode_1 = __importDefault(require("ygopro-deck-encode"));
// deprecated. Use YGOProDeck instead
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;
}
(0, assert_1.default)(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
return buffer;
const pdeck = new ygopro_deck_encode_1.default();
pdeck.main = deck.main;
pdeck.extra = [];
pdeck.side = deck.side;
return Buffer.from(pdeck.toUpdateDeckPayload());
}
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;
(0, assert_1.default)(buffer.length >= (2 + mainLength + sideLength) * 4, `Invalid buffer size. Expected: ${correctBufferLength}. Got: ${buffer.length}`);
const main = [];
const side = [];
for (let i = 0; i < mainLength; ++i) {
main.push(buffer.readInt32LE(pointer));
pointer += 4;
}
for (let i = 0; i < sideLength; ++i) {
side.push(buffer.readInt32LE(pointer));
pointer += 4;
}
return { main, side };
return ygopro_deck_encode_1.default.fromUpdateDeckPayload(buffer);
}
//# sourceMappingURL=DeckEncoder.js.map
\ No newline at end of file
import assert from "assert";
import YGOProDeck from "ygopro-deck-encode";
export interface Deck {
main: number[];
side: number[];
}
// deprecated. Use YGOProDeck instead
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;
const pdeck = new YGOProDeck();
pdeck.main = deck.main;
pdeck.extra = [];
pdeck.side = deck.side;
return Buffer.from(pdeck.toUpdateDeckPayload());
}
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};
return YGOProDeck.fromUpdateDeckPayload(buffer);
}
......@@ -33,4 +33,3 @@ exports.Ban = Ban = __decorate([
(0, typeorm_1.Entity)(),
(0, typeorm_1.Unique)(["ip", "name"])
], Ban);
//# sourceMappingURL=Ban.js.map
\ No newline at end of file
......@@ -27,4 +27,3 @@ __decorate([
(0, typeorm_1.Column)({ type: "tinyint" }),
__metadata("design:type", Number)
], BasePlayer.prototype, "pos", void 0);
//# sourceMappingURL=BasePlayer.js.map
\ No newline at end of file
......@@ -62,4 +62,3 @@ exports.CloudReplay = CloudReplay = __decorate([
}
})
], CloudReplay);
//# sourceMappingURL=CloudReplay.js.map
\ No newline at end of file
......@@ -36,4 +36,3 @@ __decorate([
exports.CloudReplayPlayer = CloudReplayPlayer = CloudReplayPlayer_1 = __decorate([
(0, typeorm_1.Entity)()
], CloudReplayPlayer);
//# sourceMappingURL=CloudReplayPlayer.js.map
\ No newline at end of file
......@@ -22,4 +22,3 @@ __decorate([
(0, typeorm_1.UpdateDateColumn)(),
__metadata("design:type", Date)
], CreateAndUpdateTimeBase.prototype, "updateTime", void 0);
//# sourceMappingURL=CreateAndUpdateTimeBase.js.map
\ No newline at end of file
......@@ -93,4 +93,3 @@ exports.DuelLog = DuelLog = __decorate([
}
})
], DuelLog);
//# sourceMappingURL=DuelLog.js.map
\ No newline at end of file
......@@ -98,4 +98,3 @@ __decorate([
exports.DuelLogPlayer = DuelLogPlayer = DuelLogPlayer_1 = __decorate([
(0, typeorm_1.Entity)()
], DuelLogPlayer);
//# sourceMappingURL=DuelLogPlayer.js.map
\ No newline at end of file
......@@ -44,4 +44,3 @@ __decorate([
exports.RandomDuelBan = RandomDuelBan = __decorate([
(0, typeorm_1.Entity)()
], RandomDuelBan);
//# sourceMappingURL=RandomDuelBan.js.map
\ No newline at end of file
......@@ -69,4 +69,3 @@ __decorate([
exports.RandomDuelScore = RandomDuelScore = __decorate([
(0, typeorm_1.Entity)()
], RandomDuelScore);
//# sourceMappingURL=RandomDuelScore.js.map
\ No newline at end of file
......@@ -26,4 +26,3 @@ __decorate([
exports.User = User = __decorate([
(0, typeorm_1.Entity)()
], User);
//# sourceMappingURL=User.js.map
\ No newline at end of file
......@@ -24,6 +24,7 @@
"20": "SURRENDER",
"21": "TIME_CONFIRM",
"22": "CHAT",
"23": "EXTERNAL_ADDRESS",
"32": "HS_TODUELIST",
"33": "HS_TOOBSERVER",
"34": "HS_READY",
......
......@@ -29,6 +29,10 @@
"replay_delay": true,
"hide_name": false,
"display_watchers": false,
"trusted_proxies": [
"127.0.0.1/8",
"::1/128"
],
"i18n": {
"auto_pick": false,
"default": "zh-cn",
......@@ -201,10 +205,6 @@
"neos": {
"enabled": false,
"port": 7977,
"trusted_proxies": [
"127.0.0.1/8",
"::1/128"
],
"ip_header": "x-forwarded-for"
},
"test_mode": {
......
......@@ -179,6 +179,8 @@
"banned_athletic_deck_part1": "Entertainment Mode does not allow top ",
"banned_athletic_deck_part2": " popular meta decks. Please change your deck.",
"chat_disabled": "Chat is disabled in this room.",
"version_to_polyfill": "Your client version is not fully supported. Please rejoin to enable temporary compatibility mode. For the best experience, we recommend updating your game to the latest version.",
"version_polyfilled": "Temporary compatibility mode has been enabled for your version. We recommend updating your game to avoid potential compatibility issues in the future.",
"using_athletic_deck": " is using a competitive deck."
},
"es-es": {
......@@ -507,6 +509,8 @@
"banned_athletic_deck_part1": "娱乐匹配中禁止使用使用数前",
"banned_athletic_deck_part2": "的竞技卡组。请更换卡组。",
"chat_disabled": "本房间禁止聊天。",
"version_to_polyfill": "当前客户端版本暂未完全支持。请重新加入以启用临时兼容模式。为获得更佳体验,建议尽快更新游戏版本。",
"version_polyfilled": "已为当前版本启用临时兼容模式。建议尽快更新游戏,以避免后续兼容性问题。",
"using_athletic_deck": " 正在使用竞技卡组。"
},
"ko-kr": {
......
......@@ -8,7 +8,8 @@
"HS_KICK":"CTOS_Kick",
"UPDATE_DECK": "deck",
"CHANGE_SIDE":"",
"CHAT": "chat"
"CHAT": "chat",
"EXTERNAL_ADDRESS": "CTOS_ExternalAddress"
},
"STOC":{
"JOIN_GAME":"STOC_JoinGame",
......
......@@ -42,6 +42,10 @@
{"name": "gameid", "type": "unsigned int"},
{"name": "pass", "type": "unsigned short", "length": 20, "encoding": "UTF-16LE"}
],
"CTOS_ExternalAddress": [
{"name": "real_ip", "type": "unsigned int"},
{"name": "hostname", "type": "unsigned short", "length":"256", "encoding": "UTF-16LE"}
],
"CTOS_Kick": [
{"name": "pos", "type": "unsigned char"}
],
......
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasePolyfiller = void 0;
class BasePolyfiller {
async polyfillGameMsg(msgTitle, buffer) {
return;
}
async polyfillResponse(msgTitle, buffer) {
return;
}
splice(buf, offset, deleteCount = 1) {
if (offset < 0 || offset >= buf.length)
return Buffer.alloc(0);
deleteCount = Math.min(deleteCount, buf.length - offset);
const end = offset + deleteCount;
const newBuf = Buffer.concat([
buf.slice(0, offset),
buf.slice(end)
]);
return newBuf;
}
insert(buf, offset, insertBuf) {
if (offset < 0)
offset = 0;
if (offset > buf.length)
offset = buf.length;
const newBuf = Buffer.concat([
buf.slice(0, offset),
insertBuf,
buf.slice(offset)
]);
return newBuf;
}
}
exports.BasePolyfiller = BasePolyfiller;
export class BasePolyfiller {
async polyfillGameMsg(msgTitle: string, buffer: Buffer): Promise<Buffer | undefined> {
return;
}
async polyfillResponse(msgTitle: string, buffer: Buffer): Promise<Buffer | undefined> {
return;
}
splice(buf: Buffer, offset: number, deleteCount = 1): Buffer {
if (offset < 0 || offset >= buf.length) return Buffer.alloc(0);
deleteCount = Math.min(deleteCount, buf.length - offset);
const end = offset + deleteCount;
const newBuf = Buffer.concat([
buf.slice(0, offset),
buf.slice(end)
]);
return newBuf;
}
insert(buf: Buffer, offset: number, insertBuf: Buffer): Buffer {
if (offset < 0) offset = 0;
if (offset > buf.length) offset = buf.length;
const newBuf = Buffer.concat([
buf.slice(0, offset),
insertBuf,
buf.slice(offset)
]);
return newBuf;
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.polyfillGameMsg = polyfillGameMsg;
exports.polyfillResponse = polyfillResponse;
const registry_1 = require("./registry");
const getPolyfillers = (version) => {
const polyfillers = [];
for (const [pVersion, polyfillerCls] of registry_1.polyfillRegistry.entries()) {
if (version <= pVersion) {
polyfillers.push({ version: pVersion, polyfiller: new polyfillerCls() });
}
}
polyfillers.sort((a, b) => a.version - b.version);
return polyfillers.map(p => p.polyfiller);
};
async function polyfillGameMsg(version, msgTitle, buffer) {
const polyfillers = getPolyfillers(version);
let pbuf = buffer;
for (const polyfiller of polyfillers) {
const newBuf = await polyfiller.polyfillGameMsg(msgTitle, pbuf);
if (newBuf) {
pbuf = newBuf;
}
}
if (pbuf === buffer) {
return undefined;
}
else if (pbuf.length <= buffer.length) {
pbuf.copy(buffer, 0, 0, pbuf.length);
return pbuf.length === buffer.length
? undefined
: buffer.slice(0, pbuf.length);
}
else {
return pbuf;
}
}
async function polyfillResponse(version, msgTitle, buffer) {
const polyfillers = getPolyfillers(version);
for (const polyfiller of polyfillers) {
await polyfiller.polyfillResponse(msgTitle, buffer);
}
}
import { BasePolyfiller } from "./base-polyfiller";
import { polyfillRegistry } from "./registry";
const getPolyfillers = (version: number) => {
const polyfillers: {version: number, polyfiller: BasePolyfiller}[] = [];
for (const [pVersion, polyfillerCls] of polyfillRegistry.entries()) {
if (version <= pVersion) {
polyfillers.push({ version: pVersion, polyfiller: new polyfillerCls() });
}
}
polyfillers.sort((a, b) => a.version - b.version);
return polyfillers.map(p => p.polyfiller);
}
export async function polyfillGameMsg(version: number, msgTitle: string, buffer: Buffer) {
const polyfillers = getPolyfillers(version);
let pbuf = buffer;
for (const polyfiller of polyfillers) {
const newBuf = await polyfiller.polyfillGameMsg(msgTitle, pbuf);
if (newBuf) {
pbuf = newBuf;
}
}
if (pbuf === buffer) {
return undefined;
} else if (pbuf.length <= buffer.length) {
pbuf.copy(buffer, 0, 0, pbuf.length);
return pbuf.length === buffer.length
? undefined
: buffer.slice(0, pbuf.length);
} else {
return pbuf;
}
}
export async function polyfillResponse(version: number, msgTitle: string, buffer: Buffer) {
const polyfillers = getPolyfillers(version);
for (const polyfiller of polyfillers) {
await polyfiller.polyfillResponse(msgTitle, buffer);
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Polyfiller1361 = exports.gcd = void 0;
const base_polyfiller_1 = require("../base-polyfiller");
const gcd = (nums) => {
const gcdTwo = (a, b) => {
if (b === 0)
return a;
return gcdTwo(b, a % b);
};
return nums.reduce((acc, num) => gcdTwo(acc, num));
};
exports.gcd = gcd;
class Polyfiller1361 extends base_polyfiller_1.BasePolyfiller {
async polyfillGameMsg(msgTitle, buffer) {
if (msgTitle === 'CONFIRM_CARDS') {
// buf[0]: MSG_CONFIRM_CARDS
// buf[1]: playerid
// buf[2]: ADDED skip_panel
return this.splice(buffer, 2, 1);
}
else if (msgTitle === 'SELECT_CHAIN') {
// buf[0]: MSG_SELECT_CHAIN
// buf[1]: playerid
// buf[2]: size
// buf[3]: spe_count
// buf[REMOVED]: forced
// buf[4-7]: hint_timing player
// buf[8-11]: hint_timing 1-player
// then it's 14 bytes for each item
// item[0]: not related
// item[1]: ADDED forced
// item[2-13] not related
const size = buffer[2];
const itemStartOffset = 12; // after the header (up to hint timings)
// 判断是否存在任何 item 的 forced = 1(在原始 buffer 中判断)
let anyForced = false;
for (let i = 0; i < size; i++) {
const itemOffset = itemStartOffset + i * 14;
const forced = buffer[itemOffset + 1];
if (forced === 1) {
anyForced = true;
break;
}
}
// 从后往前 splice 每个 item 的 forced 字段
for (let i = size - 1; i >= 0; i--) {
const itemOffset = itemStartOffset + i * 14;
buffer = this.splice(buffer, itemOffset + 1, 1); // 删除每个 item 的 forced(第 1 字节)
}
// 最后再插入旧版所需的 forced 标志
return this.insert(buffer, 4, Buffer.from([anyForced ? 1 : 0]));
}
else if (msgTitle === 'SELECT_SUM') {
// buf[0]: MSG_SELECT_SUM
// buf[1]: 0 => equal, 1 => greater
// buf[2]: playerid
// buf[3-6]: target_value
// buf[7]: min
// buf[8]: max
// buf[9]: forced_count
// then each item 11 bytes
// item[0-3] code
// item[4] controler
// item[5] location
// item[6] sequence
// item[7-10] value
// item[10 + forced_count * 11] card_count
// same as above items
const targetValue = buffer.readUInt32LE(3);
const forcedCount = buffer[9];
const cardCount = buffer[10 + forcedCount * 11];
const valueOffsets = [];
for (let i = 0; i < forcedCount; i++) {
const itemOffset = 10 + i * 11;
valueOffsets.push(itemOffset + 7);
}
for (let i = 0; i < cardCount; i++) {
const itemOffset = 11 + forcedCount * 11 + i * 11;
valueOffsets.push(itemOffset + 7);
}
const values = valueOffsets.map(offset => ({
offset,
value: buffer.readUInt32LE(offset),
}));
if (!values.some(v => v.value & 0x80000000)) {
return;
}
const gcds = [targetValue];
for (const { value } of values) {
if (value & 0x80000000) {
gcds.push(value & 0x7FFFFFFF);
}
else {
const op1 = value & 0xffff;
const op2 = (value >>> 16) & 0xffff;
[op1, op2].filter(v => v > 0)
.forEach(v => gcds.push(v));
}
}
const gcdValue = (0, exports.gcd)(gcds);
buffer.writeUInt32LE(targetValue / gcdValue, 3);
for (const trans of values) {
let target = 0;
const { value } = trans;
if (value & 0x80000000) {
target = ((value & 0x7FFFFFFF) / gcdValue) & 0xffff;
}
else {
// shrink op1 and op2
const op1 = value & 0xffff;
const op2 = (value >>> 16) & 0xffff;
target = ((op1 / gcdValue) & 0xffff) | ((((op2 / gcdValue) & 0xffff) << 16) >>> 0);
}
buffer.writeUInt32LE(target, trans.offset);
}
}
return;
}
}
exports.Polyfiller1361 = Polyfiller1361;
import { BasePolyfiller } from "../base-polyfiller";
export const gcd = (nums: number[]) => {
const gcdTwo = (a: number, b: number) => {
if (b === 0) return a;
return gcdTwo(b, a % b);
};
return nums.reduce((acc, num) => gcdTwo(acc, num));
}
export class Polyfiller1361 extends BasePolyfiller {
async polyfillGameMsg(msgTitle: string, buffer: Buffer) {
if (msgTitle === 'CONFIRM_CARDS') {
// buf[0]: MSG_CONFIRM_CARDS
// buf[1]: playerid
// buf[2]: ADDED skip_panel
return this.splice(buffer, 2, 1);
} else if (msgTitle === 'SELECT_CHAIN') {
// buf[0]: MSG_SELECT_CHAIN
// buf[1]: playerid
// buf[2]: size
// buf[3]: spe_count
// buf[REMOVED]: forced
// buf[4-7]: hint_timing player
// buf[8-11]: hint_timing 1-player
// then it's 14 bytes for each item
// item[0]: not related
// item[1]: ADDED forced
// item[2-13] not related
const size = buffer[2];
const itemStartOffset = 12; // after the header (up to hint timings)
// 判断是否存在任何 item 的 forced = 1(在原始 buffer 中判断)
let anyForced = false;
for (let i = 0; i < size; i++) {
const itemOffset = itemStartOffset + i * 14;
const forced = buffer[itemOffset + 1];
if (forced === 1) {
anyForced = true;
break;
}
}
// 从后往前 splice 每个 item 的 forced 字段
for (let i = size - 1; i >= 0; i--) {
const itemOffset = itemStartOffset + i * 14;
buffer = this.splice(buffer, itemOffset + 1, 1); // 删除每个 item 的 forced(第 1 字节)
}
// 最后再插入旧版所需的 forced 标志
return this.insert(buffer, 4, Buffer.from([anyForced ? 1 : 0]));
} else if (msgTitle === 'SELECT_SUM') {
// buf[0]: MSG_SELECT_SUM
// buf[1]: 0 => equal, 1 => greater
// buf[2]: playerid
// buf[3-6]: target_value
// buf[7]: min
// buf[8]: max
// buf[9]: forced_count
// then each item 11 bytes
// item[0-3] code
// item[4] controler
// item[5] location
// item[6] sequence
// item[7-10] value
// item[10 + forced_count * 11] card_count
// same as above items
const targetValue = buffer.readUInt32LE(3);
const forcedCount = buffer[9];
const cardCount = buffer[10 + forcedCount * 11];
const valueOffsets: number[] = [];
for(let i = 0; i < forcedCount; i++) {
const itemOffset = 10 + i * 11;
valueOffsets.push(itemOffset + 7);
}
for(let i = 0; i < cardCount; i++) {
const itemOffset = 11 + forcedCount * 11 + i * 11;
valueOffsets.push(itemOffset + 7);
}
const values = valueOffsets.map(offset => ({
offset,
value: buffer.readUInt32LE(offset),
}));
if (!values.some(v => v.value & 0x80000000)) {
return;
}
const gcds = [targetValue];
for(const { value } of values) {
if (value & 0x80000000) {
gcds.push(value & 0x7FFFFFFF);
} else {
const op1 = value & 0xffff;
const op2 = (value >>> 16) & 0xffff;
[op1, op2].filter(v => v > 0)
.forEach(v => gcds.push(v));
}
}
const gcdValue = gcd(gcds);
buffer.writeUInt32LE(targetValue / gcdValue, 3);
for (const trans of values) {
let target = 0;
const { value } = trans;
if (value & 0x80000000) {
target = ((value & 0x7FFFFFFF) / gcdValue) & 0xffff;
} else {
// shrink op1 and op2
const op1 = value & 0xffff;
const op2 = (value >>> 16) & 0xffff;
target = ((op1 / gcdValue) & 0xffff) | ((((op2 / gcdValue) & 0xffff) << 16) >>> 0);
}
buffer.writeUInt32LE(target, trans.offset);
}
}
return;
}
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.polyfillRegistry = void 0;
const _0x1361_1 = require("./polyfillers/0x1361");
exports.polyfillRegistry = new Map();
const addPolyfiller = (version, polyfiller) => {
exports.polyfillRegistry.set(version, polyfiller);
};
addPolyfiller(0x1361, _0x1361_1.Polyfiller1361);
import { BasePolyfiller } from "./base-polyfiller";
import { Polyfiller1361 } from "./polyfillers/0x1361";
export const polyfillRegistry = new Map<number, typeof BasePolyfiller>();
const addPolyfiller = (version: number, polyfiller: typeof BasePolyfiller) => {
polyfillRegistry.set(version, polyfiller);
}
addPolyfiller(0x1361, Polyfiller1361);
......@@ -9,6 +9,7 @@
"version": "2.3.3",
"license": "AGPL-3.0",
"dependencies": {
"aragami": "^1.2.5",
"async": "^3.2.0",
"axios": "^0.19.2",
"bunyan": "^1.8.14",
......@@ -32,16 +33,18 @@
"typeorm": "^0.2.29",
"underscore": "^1.11.0",
"underscore.string": "^3.3.6",
"ws": "^8.9.0"
"ws": "^8.9.0",
"ygopro-deck-encode": "^1.0.9"
},
"devDependencies": {
"@types/bunyan": "^1.8.8",
"@types/ip6addr": "^0.2.3",
"@types/node": "^17.0.19",
"@types/lzma": "^2.3.0",
"@types/node": "^16.18.126",
"@types/underscore": "^1.11.4",
"@types/ws": "^8.5.3",
"coffeescript": "^2.6.1",
"typescript": "^4.5.5"
"coffeescript": "^2.7.0",
"typescript": "^5.8.3"
}
},
"node_modules/@babel/code-frame": {
......@@ -67,6 +70,24 @@
"js-tokens": "^4.0.0"
}
},
"node_modules/@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==",
"license": "MIT"
},
"node_modules/@sesamecare-oss/redlock": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@sesamecare-oss/redlock/-/redlock-1.4.0.tgz",
"integrity": "sha512-2z589R+yxKLN4CgKxP1oN4dsg6Y548SE4bVYam/R0kHk7Q9VrQ9l66q+k1ehhSLLY4or9hcchuF9/MhuuZdjJg==",
"license": "UNLICENSED",
"engines": {
"node": ">=16"
},
"peerDependencies": {
"ioredis": ">=5"
}
},
"node_modules/@sqltools/formatter": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz",
......@@ -87,11 +108,22 @@
"integrity": "sha512-oe7hzc+P9DU6+gql8+bLKuUf4WL4aakyCSXZMZq2cjhhGK75qYwH1zJ4s94XOlnb4cAhrGKwnbrmMBaqDK8+Ww==",
"dev": true
},
"node_modules/@types/lzma": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@types/lzma/-/lzma-2.3.0.tgz",
"integrity": "sha512-z7TknP6ts5GPnN7P2bkZC1B/tMpoMJXG3UnPY1XDrwBq1OJQ2EpHtEypFwq5Q0A5iS37+oc+MT/o/B7x5lgl8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "17.0.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.19.tgz",
"integrity": "sha512-PfeQhvcMR4cPFVuYfBN4ifG7p9c+Dlh3yUZR6k+5yQK7wX3gDgVxBly4/WkBRs9x4dmcy1TVl08SY67wwtEvmA==",
"dev": true
"version": "16.18.126",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz",
"integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/underscore": {
"version": "1.11.4",
......@@ -165,6 +197,41 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"node_modules/aragami": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/aragami/-/aragami-1.2.5.tgz",
"integrity": "sha512-w+jSXxXYAk3X/TSMoVfc12lY1c40MYqf6W6R2yGE1q7pY4ZC+xpzGBT+Bqa4hO6hlWsM5sFRnKvAE+72HCkT2w==",
"license": "MIT",
"dependencies": {
"@sesamecare-oss/redlock": "^1.4.0",
"better-lock": "^2.0.3",
"class-transformer": "^0.5.1",
"encoded-buffer": "^0.2.6",
"generic-pool": "^3.9.0",
"ioredis": "^5.2.3",
"lodash": "^4.17.21",
"lru-cache": "^7.13.1",
"typed-reflector": "^1.0.11"
}
},
"node_modules/aragami/node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/aragami/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
......@@ -213,6 +280,21 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
"license": "MIT",
"dependencies": {
"possible-typed-array-names": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
......@@ -267,6 +349,12 @@
"tweetnacl": "^0.14.3"
}
},
"node_modules/better-lock": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/better-lock/-/better-lock-2.0.3.tgz",
"integrity": "sha512-3bCaToLrmEXZcIOOVWgi1STvp3/6EpoZAmlWBeuX2MvDB0Ql2ctl/vQ0CbhQIJYQiptdGypllP3ez+TeEmdnKQ==",
"license": "MIT"
},
"node_modules/bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
......@@ -361,6 +449,53 @@
"safe-json-stringify": "~1"
}
},
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.0",
"es-define-property": "^1.0.0",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
......@@ -403,6 +538,12 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/class-transformer": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
"license": "MIT"
},
"node_modules/cli-highlight": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.4.tgz",
......@@ -595,6 +736,15 @@
"node": ">=8"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
......@@ -604,10 +754,11 @@
}
},
"node_modules/coffeescript": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.6.1.tgz",
"integrity": "sha512-GG5nkF93qII8HmHqnnibkgpp/SV7PSnSPiWsbinwya7nNOe95aE/x2xrKZJFks8Qpko3TNrC+/LahaKgrz5YCg==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz",
"integrity": "sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A==",
"dev": true,
"license": "MIT",
"bin": {
"cake": "bin/cake",
"coffee": "bin/coffee"
......@@ -707,6 +858,23 @@
"node": ">=0.10.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
......@@ -720,6 +888,15 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
......@@ -752,6 +929,20 @@
"node": ">=0.10"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
......@@ -766,6 +957,18 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/encoded-buffer": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/encoded-buffer/-/encoded-buffer-0.2.6.tgz",
"integrity": "sha512-zEskqXs0FbF9HcwZkumyAoiB3NN23yAoJvPmLP0NuWQLXTeCDMeVRYK1kjIsZPkoXE2cIBS0iht95pqf8UKyog==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"license": "MIT",
"dependencies": {
"lodash": "^4.17.10",
"to-buffer": "^1.1.1",
"tslib": "^1.9.3"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
......@@ -774,6 +977,36 @@
"is-arrayish": "^0.2.1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
......@@ -874,6 +1107,21 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"license": "MIT",
"dependencies": {
"is-callable": "^1.2.7"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
......@@ -942,6 +1190,15 @@
"node": ">=0.4.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
......@@ -1028,6 +1285,43 @@
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
......@@ -1051,6 +1345,18 @@
"node": "*"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/graceful-fs": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.1.14.tgz",
......@@ -1101,11 +1407,62 @@
"node": ">=4"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/highlight.js": {
"version": "9.18.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz",
......@@ -1195,6 +1552,53 @@
"node": "*"
}
},
"node_modules/ioredis": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz",
"integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==",
"license": "MIT",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ioredis"
}
},
"node_modules/ioredis/node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/ioredis/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/ip6addr": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/ip6addr/-/ip6addr-0.2.5.tgz",
......@@ -1228,6 +1632,18 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
......@@ -1239,6 +1655,21 @@
"node": ">=0.10.0"
}
},
"node_modules/is-typed-array": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"license": "MIT",
"dependencies": {
"which-typed-array": "^1.1.16"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
......@@ -1386,6 +1817,24 @@
"node": ">=8"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"license": "MIT"
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
......@@ -1399,6 +1848,15 @@
"lzma.js": "bin/lzma.js"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
......@@ -2035,6 +2493,15 @@
"split": "^1.0.0"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/postgres-array": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz",
......@@ -2156,6 +2623,27 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"license": "MIT",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/reflect-metadata": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
......@@ -2264,6 +2752,23 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/set-immediate-shim": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
......@@ -2363,6 +2868,12 @@
"node": ">=0.10.0"
}
},
"node_modules/standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
"license": "MIT"
},
"node_modules/stream-buffers": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.6.tgz",
......@@ -2492,6 +3003,26 @@
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"node_modules/to-buffer": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
"integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
"license": "MIT",
"dependencies": {
"isarray": "^2.0.5",
"safe-buffer": "^5.2.1",
"typed-array-buffer": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/to-buffer/node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"license": "MIT"
},
"node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
......@@ -2541,6 +3072,29 @@
"node": ">=8"
}
},
"node_modules/typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.3",
"es-errors": "^1.3.0",
"is-typed-array": "^1.1.14"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/typed-reflector": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/typed-reflector/-/typed-reflector-1.0.11.tgz",
"integrity": "sha512-OhryVYaR+tBEW9Yt2PsPqAniNfbVk1idKbnLxBCBPUSHVRm+Ajik/QxifoJUuGoaXAZDLW9JlJTO6ctXGZX9gQ==",
"license": "MIT",
"dependencies": {
"reflect-metadata": "^0.1.13"
}
},
"node_modules/typeorm": {
"version": "0.2.29",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.29.tgz",
......@@ -2682,16 +3236,17 @@
}
},
"node_modules/typescript": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
"node": ">=14.17"
}
},
"node_modules/underscore": {
......@@ -2773,6 +3328,27 @@
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"node_modules/which-typed-array": {
"version": "1.1.19",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
"integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
"call-bound": "^1.0.4",
"for-each": "^0.3.5",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
......@@ -3108,6 +3684,12 @@
"engines": {
"node": ">=10"
}
},
"node_modules/ygopro-deck-encode": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/ygopro-deck-encode/-/ygopro-deck-encode-1.0.9.tgz",
"integrity": "sha512-2aw/Lr8Sg4cPXKgq71Zk/GQPTZy5GhmviptVHWqMGEW0E2qTaxwpGmsQAN2Q4OWaK1lP+3g3bZt9BaqmWYZQSw==",
"license": "MIT"
}
},
"dependencies": {
......@@ -3134,6 +3716,17 @@
"js-tokens": "^4.0.0"
}
},
"@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
},
"@sesamecare-oss/redlock": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@sesamecare-oss/redlock/-/redlock-1.4.0.tgz",
"integrity": "sha512-2z589R+yxKLN4CgKxP1oN4dsg6Y548SE4bVYam/R0kHk7Q9VrQ9l66q+k1ehhSLLY4or9hcchuF9/MhuuZdjJg==",
"requires": {}
},
"@sqltools/formatter": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz",
......@@ -3154,10 +3747,19 @@
"integrity": "sha512-oe7hzc+P9DU6+gql8+bLKuUf4WL4aakyCSXZMZq2cjhhGK75qYwH1zJ4s94XOlnb4cAhrGKwnbrmMBaqDK8+Ww==",
"dev": true
},
"@types/lzma": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@types/lzma/-/lzma-2.3.0.tgz",
"integrity": "sha512-z7TknP6ts5GPnN7P2bkZC1B/tMpoMJXG3UnPY1XDrwBq1OJQ2EpHtEypFwq5Q0A5iS37+oc+MT/o/B7x5lgl8Q==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "17.0.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.19.tgz",
"integrity": "sha512-PfeQhvcMR4cPFVuYfBN4ifG7p9c+Dlh3yUZR6k+5yQK7wX3gDgVxBly4/WkBRs9x4dmcy1TVl08SY67wwtEvmA==",
"version": "16.18.126",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz",
"integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==",
"dev": true
},
"@types/underscore": {
......@@ -3219,6 +3821,34 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"aragami": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/aragami/-/aragami-1.2.5.tgz",
"integrity": "sha512-w+jSXxXYAk3X/TSMoVfc12lY1c40MYqf6W6R2yGE1q7pY4ZC+xpzGBT+Bqa4hO6hlWsM5sFRnKvAE+72HCkT2w==",
"requires": {
"@sesamecare-oss/redlock": "^1.4.0",
"better-lock": "^2.0.3",
"class-transformer": "^0.5.1",
"encoded-buffer": "^0.2.6",
"generic-pool": "^3.9.0",
"ioredis": "^5.2.3",
"lodash": "^4.17.21",
"lru-cache": "^7.13.1",
"typed-reflector": "^1.0.11"
},
"dependencies": {
"generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="
},
"lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="
}
}
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
......@@ -3266,6 +3896,14 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
"requires": {
"possible-typed-array-names": "^1.0.0"
}
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
......@@ -3302,6 +3940,11 @@
"tweetnacl": "^0.14.3"
}
},
"better-lock": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/better-lock/-/better-lock-2.0.3.tgz",
"integrity": "sha512-3bCaToLrmEXZcIOOVWgi1STvp3/6EpoZAmlWBeuX2MvDB0Ql2ctl/vQ0CbhQIJYQiptdGypllP3ez+TeEmdnKQ=="
},
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
......@@ -3364,6 +4007,35 @@
"safe-json-stringify": "~1"
}
},
"call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
"requires": {
"call-bind-apply-helpers": "^1.0.0",
"es-define-property": "^1.0.0",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.2"
}
},
"call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"requires": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
}
},
"call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"requires": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
......@@ -3397,6 +4069,11 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"class-transformer": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
},
"cli-highlight": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.4.tgz",
......@@ -3541,15 +4218,20 @@
}
}
},
"cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"coffeescript": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.6.1.tgz",
"integrity": "sha512-GG5nkF93qII8HmHqnnibkgpp/SV7PSnSPiWsbinwya7nNOe95aE/x2xrKZJFks8Qpko3TNrC+/LahaKgrz5YCg==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz",
"integrity": "sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A==",
"dev": true
},
"color-convert": {
......@@ -3624,6 +4306,16 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
},
"define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"requires": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
......@@ -3634,6 +4326,11 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
......@@ -3653,6 +4350,16 @@
"nan": "^2.14.0"
}
},
"dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"requires": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
}
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
......@@ -3667,6 +4374,16 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"encoded-buffer": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/encoded-buffer/-/encoded-buffer-0.2.6.tgz",
"integrity": "sha512-zEskqXs0FbF9HcwZkumyAoiB3NN23yAoJvPmLP0NuWQLXTeCDMeVRYK1kjIsZPkoXE2cIBS0iht95pqf8UKyog==",
"requires": {
"lodash": "^4.17.10",
"to-buffer": "^1.1.1",
"tslib": "^1.9.3"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
......@@ -3675,6 +4392,24 @@
"is-arrayish": "^0.2.1"
}
},
"es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
},
"es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
},
"es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"requires": {
"es-errors": "^1.3.0"
}
},
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
......@@ -3752,6 +4487,14 @@
}
}
},
"for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
"requires": {
"is-callable": "^1.2.7"
}
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
......@@ -3806,6 +4549,11 @@
}
}
},
"function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
......@@ -3878,6 +4626,32 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"requires": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
}
},
"get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"requires": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
......@@ -3898,6 +4672,11 @@
"path-is-absolute": "^1.0.0"
}
},
"gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
},
"graceful-fs": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.1.14.tgz",
......@@ -3931,11 +4710,40 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"requires": {
"es-define-property": "^1.0.0"
}
},
"has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
},
"has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"requires": {
"has-symbols": "^1.0.3"
}
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"requires": {
"function-bind": "^1.1.2"
}
},
"highlight.js": {
"version": "9.18.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz",
......@@ -3996,6 +4804,37 @@
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
},
"ioredis": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.1.tgz",
"integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==",
"requires": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"dependencies": {
"debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"requires": {
"ms": "^2.1.3"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}
}
},
"ip6addr": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/ip6addr/-/ip6addr-0.2.5.tgz",
......@@ -4028,6 +4867,11 @@
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
......@@ -4036,6 +4880,14 @@
"number-is-nan": "^1.0.0"
}
},
"is-typed-array": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"requires": {
"which-typed-array": "^1.1.16"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
......@@ -4167,6 +5019,21 @@
"p-locate": "^4.1.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
},
"lru-cache": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
......@@ -4177,6 +5044,11 @@
"resolved": "https://registry.npmjs.org/lzma/-/lzma-2.3.2.tgz",
"integrity": "sha1-N4OySFi5wOdHoN88vx+1/KqSxEE="
},
"math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
......@@ -4686,6 +5558,11 @@
"split": "^1.0.0"
}
},
"possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="
},
"postgres-array": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz",
......@@ -4780,6 +5657,19 @@
}
}
},
"redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="
},
"redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"requires": {
"redis-errors": "^1.0.0"
}
},
"reflect-metadata": {
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
......@@ -4861,6 +5751,19 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"requires": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
}
},
"set-immediate-shim": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
......@@ -4929,6 +5832,11 @@
"tweetnacl": "~0.14.0"
}
},
"standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
},
"stream-buffers": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.6.tgz",
......@@ -5037,6 +5945,23 @@
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"to-buffer": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz",
"integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
"requires": {
"isarray": "^2.0.5",
"safe-buffer": "^5.2.1",
"typed-array-buffer": "^1.0.3"
},
"dependencies": {
"isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
}
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
......@@ -5074,6 +5999,24 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="
},
"typed-array-buffer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
"integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
"requires": {
"call-bound": "^1.0.3",
"es-errors": "^1.3.0",
"is-typed-array": "^1.1.14"
}
},
"typed-reflector": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/typed-reflector/-/typed-reflector-1.0.11.tgz",
"integrity": "sha512-OhryVYaR+tBEW9Yt2PsPqAniNfbVk1idKbnLxBCBPUSHVRm+Ajik/QxifoJUuGoaXAZDLW9JlJTO6ctXGZX9gQ==",
"requires": {
"reflect-metadata": "^0.1.13"
}
},
"typeorm": {
"version": "0.2.29",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.29.tgz",
......@@ -5169,9 +6112,9 @@
}
},
"typescript": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true
},
"underscore": {
......@@ -5240,6 +6183,20 @@
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"which-typed-array": {
"version": "1.1.19",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
"integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
"requires": {
"available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8",
"call-bound": "^1.0.4",
"for-each": "^0.3.5",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-tostringtag": "^1.0.2"
}
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
......@@ -5486,6 +6443,11 @@
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
},
"ygopro-deck-encode": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/ygopro-deck-encode/-/ygopro-deck-encode-1.0.9.tgz",
"integrity": "sha512-2aw/Lr8Sg4cPXKgq71Zk/GQPTZy5GhmviptVHWqMGEW0E2qTaxwpGmsQAN2Q4OWaK1lP+3g3bZt9BaqmWYZQSw=="
}
}
}
......@@ -11,6 +11,7 @@
],
"author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>, Nanahira <78877@qq.com>",
"dependencies": {
"aragami": "^1.2.5",
"async": "^3.2.0",
"axios": "^0.19.2",
"bunyan": "^1.8.14",
......@@ -34,7 +35,8 @@
"typeorm": "^0.2.29",
"underscore": "^1.11.0",
"underscore.string": "^3.3.6",
"ws": "^8.9.0"
"ws": "^8.9.0",
"ygopro-deck-encode": "^1.0.9"
},
"license": "AGPL-3.0",
"scripts": {
......@@ -48,10 +50,11 @@
"devDependencies": {
"@types/bunyan": "^1.8.8",
"@types/ip6addr": "^0.2.3",
"@types/node": "^17.0.19",
"@types/lzma": "^2.3.0",
"@types/node": "^16.18.126",
"@types/underscore": "^1.11.4",
"@types/ws": "^8.5.3",
"coffeescript": "^2.6.1",
"typescript": "^4.5.5"
"coffeescript": "^2.7.0",
"typescript": "^5.8.3"
}
}
......@@ -5,14 +5,15 @@
"esModuleInterop": true,
"resolveJsonModule": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true
"experimentalDecorators": true
},
"compileOnSave": true,
"allowJs": true,
"include": [
"*.ts",
"data-manager/*.ts",
"data-manager/entities/*.ts"
"data-manager/entities/*.ts",
"msg-polyfill/*.ts",
"msg-polyfill/polyfillers/*.ts"
]
}
......@@ -18,4 +18,3 @@ async function retry(fn, count, delayFn = (attempt) => Math.pow(2, attempt) * 10
// 如果全部尝试失败,抛出最后一个错误
throw lastError;
}
//# sourceMappingURL=utility.js.map
\ No newline at end of file
......@@ -80,7 +80,8 @@ import_datas = global.import_datas = [
"ready_trap",
"join_time",
"arena_quit_free",
"replays_sent"
"replays_sent",
"actual_version",
]
merge = require 'deepmerge'
......@@ -93,6 +94,16 @@ util = require("util")
Q = require("q")
YGOProDeck = require('ygopro-deck-encode').default
Aragami = require('aragami').Aragami
aragami = global.aragami = new Aragami() # we use memory mode only
aragami_classes = global.aragami_classes = require('./aragami-classes.js')
msg_polyfill = global.msg_polyfill = require('./msg-polyfill/index.js')
#heapdump = require 'heapdump'
checkFileExists = (path) =>
......@@ -320,6 +331,10 @@ init = () ->
if settings.modules.hide_name == true
settings.modules.hide_name = "start"
imported = true
if settings.modules.neos.trusted_proxies
settings.modules.trusted_proxies = settings.modules.neos.trusted_proxies
delete settings.modules.neos.trusted_proxies
imported = true
#finish
keysFromEnv = Object.keys(process.env).filter((key) => key.startsWith('SRVPRO_'))
if keysFromEnv.length > 0
......@@ -442,7 +457,7 @@ init = () ->
long_resolve_cards = global.long_resolve_cards = await loadJSONAsync('./data/long_resolve_cards.json')
if settings.modules.tournament_mode.enable_recover
ReplayParser = global.ReplayParser = require "./Replay.js"
ReplayParser = global.ReplayParser = (require "./Replay.js").Replay
if settings.modules.athletic_check.enabled
AthleticChecker = require("./athletic-check.js").AthleticChecker
......@@ -1189,6 +1204,56 @@ CLIENT_send_replays_and_kick = global.CLIENT_send_replays_and_kick = (client, ro
CLIENT_kick(client)
return
toIpv4 = global.toIpv4 = (ip) ->
if ip.startsWith('::ffff:')
return ip.slice(7)
return ip
toIpv6 = global.toIpv6 = (ip) ->
if /^(\d{1,3}\.){3}\d{1,3}$/.test(ip)
return '::ffff:' + ip
return ip
isTrustedProxy = global.isTrustedProxy = (ip) ->
return settings.modules.trusted_proxies.some((trusted) ->
cidr = if trusted.includes('/') then ip6addr.createCIDR(trusted) else ip6addr.createAddrRange(trusted, trusted)
return cidr.contains(ip)
)
getRealIp = global.getRealIp = (physical_ip, xff_ip) ->
if not xff_ip or xff_ip == physical_ip
return toIpv6(physical_ip)
if isTrustedProxy(physical_ip)
return toIpv6(xff_ip.split(',')[0].trim())
log.warn("Untrusted proxy detected: #{physical_ip} -> #{xff_ip}")
return toIpv6(physical_ip)
CLIENT_set_ip = global.CLIENT_set_ip = (client, xff_ip) ->
client_prev_ip = client.ip
client.ip = getRealIp(client.physical_ip, xff_ip)
if client_prev_ip == client.ip
return false
if client_prev_ip and ROOM_connected_ip[client_prev_ip] and ROOM_connected_ip[client_prev_ip] > 0
ROOM_connected_ip[client_prev_ip]--
if ROOM_connected_ip[client_prev_ip] <= 0
delete ROOM_connected_ip[client_prev_ip]
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
connect_count = ROOM_connected_ip[client.ip] or 0
if !settings.modules.test_mode.no_connect_count_limit and !client.is_local and !isTrustedProxy(client.ip)
connect_count++
ROOM_connected_ip[client.ip] = connect_count
# log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
client.destroy()
return true
return false
SOCKET_flush_data = global.SOCKET_flush_data = (sk, datas) ->
if !sk or sk.isClosed
return false
......@@ -1197,9 +1262,6 @@ SOCKET_flush_data = global.SOCKET_flush_data = (sk, datas) ->
await ygopro.helper.send(sk, buffer)
return true
getSeedTimet = global.getSeedTimet = (count) ->
return _.range(count).map(() => 0)
class Room
constructor: (name, @hostinfo) ->
@name = name
......@@ -1368,12 +1430,11 @@ class Room
@hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode]
if firstSeed
param.push(firstSeed)
seeds = getSeedTimet(2)
param.push(seeds[i]) for i in [0...2]
else
seeds = getSeedTimet(3)
param.push(seeds[i]) for i in [0...3]
# new replay with extended header and long seed
firstSeedBuf = Buffer.allocUnsafe(firstSeed.length * 4)
for i in [0...firstSeed.length]
firstSeedBuf.writeUInt32LE(firstSeed[i], i * 4)
param.push(firstSeedBuf.toString('base64'))
try
@process = spawn './ygopro', param, {cwd: 'ygopro'}
......@@ -1526,7 +1587,12 @@ class Room
return false
try
@recover_replay = await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName)
@spawn(@recover_replay.header.seed)
if !@recover_replay.header.seedSequence.length
# it's old replay, unsupported
log.warn("LOAD RECOVER REPLAY FAIL: Old replay format, unsupported", @recover_duel_log.replayFileName)
@terminate()
return false
@spawn(@recover_replay.header.seedSequence)
return true
catch e
log.warn("LOAD RECOVER REPLAY FAIL", e.toString())
......@@ -1841,18 +1907,8 @@ class Room
# 网络连接
netRequestHandler = (client) ->
if !client.isWs
client.ip = client.remoteAddress or ''
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
connect_count = ROOM_connected_ip[client.ip] or 0
if !settings.modules.test_mode.no_connect_count_limit and !client.is_local
connect_count++
ROOM_connected_ip[client.ip] = connect_count
#log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if ROOM_bad_ip[client.ip] > 5 or ROOM_connected_ip[client.ip] > 10
log.info 'BAD IP', client.ip
client.destroy()
client.physical_ip = client.remoteAddress or ""
if CLIENT_set_ip(client)
return
# server stand for the connection to ygopro server process
......@@ -1870,8 +1926,11 @@ netRequestHandler = (client) ->
return
room=ROOM_all[client.rid]
connect_count = ROOM_connected_ip[client.ip]
if connect_count > 0
if connect_count and connect_count > 0
connect_count--
if connect_count == 0
delete ROOM_connected_ip[client.ip]
else
ROOM_connected_ip[client.ip] = connect_count
client.isClosed = true
if settings.modules.heartbeat_detection.enabled
......@@ -1975,8 +2034,8 @@ netRequestHandler = (client) ->
preconnect = false
if settings.modules.reconnect.enabled and client.pre_reconnecting_to_room
ctos_filter = ["UPDATE_DECK"]
if client.name == null
ctos_filter = ["JOIN_GAME", "PLAYER_INFO"]
else if client.name == null
ctos_filter = ["EXTERNAL_ADDRESS", "JOIN_GAME", "PLAYER_INFO"]
preconnect = true
handle_data = await ygopro.helper.handleBuffer(ctos_buffer, "CTOS", ctos_filter, {
client: client,
......@@ -2032,6 +2091,19 @@ deck_name_match = global.deck_name_match = (deck_name, player_name) ->
# 功能模块
# return true to cancel a synchronous message
ygopro.ctos_follow 'EXTERNAL_ADDRESS', true, (buffer, info, client, server, datas)->
ip_uint = buffer.readUInt32BE(0)
if ip_uint == 0
return false
ip_parts = [
(ip_uint >>> 24) & 0xFF,
(ip_uint >>> 16) & 0xFF,
(ip_uint >>> 8) & 0xFF,
ip_uint & 0xFF
]
xff_ip = ip_parts.join('.')
return CLIENT_set_ip(client, xff_ip)
ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)->
# second PLAYER_INFO = attack
if client.name
......@@ -2078,6 +2150,44 @@ ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)->
await return false
ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
check_version = () ->
bad_version = (msg) ->
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.RED)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 4
code: settings.version
}
CLIENT_kick(client)
return false
client_key = CLIENT_get_authorize_key(client)
clean_blocker = () ->
aragami.del(aragami_classes.ClientVersionBlocker, client_key)
if info.version == settings.version
await clean_blocker()
return true
if settings.alternative_versions.includes(info.version)
if !await aragami.has(aragami_classes.ClientVersionBlocker, client_key)
blocker_obj = new aragami_classes.ClientVersionBlocker()
blocker_obj.clientKey = client_key
await aragami.set(blocker_obj)
return bad_version("${version_to_polyfill}")
else
await clean_blocker()
return true
return bad_version(if info.version < settings.version then settings.modules.update else settings.modules.wait_update)
polyfill_version = () ->
if client.actual_version
# already polyfilled
return
client.actual_version = info.version
if info.version != settings.version and settings.alternative_versions.includes(info.version)
info.version = settings.version
struct = ygopro.structs.get("CTOS_JoinGame")
struct._setBuff(buffer)
struct.set("version", info.version)
buffer = struct.buffer
ygopro.stoc_send_chat(client, "${version_polyfilled}", ygopro.constants.COLORS.BABYBLUE)
await return
#log.info info
info.pass=info.pass.trim()
client.pass = info.pass
......@@ -2086,9 +2196,10 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
return
else if settings.modules.stop
ygopro.stoc_die(client, settings.modules.stop)
return
else if info.pass == "Marshtomp" or info.pass == "the Big Brother"
ygopro.stoc_die(client, "${bad_user_name}")
return
else if info.pass.toUpperCase()=="R" and settings.modules.cloud_replay.enabled
ygopro.stoc_send_chat(client,"${cloud_replay_hint}", ygopro.constants.COLORS.BABYBLUE)
replays = await dataManager.getCloudReplaysFromKey(CLIENT_get_authorize_key(client))
......@@ -2099,7 +2210,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
code: 9
}
CLIENT_kick(client)
return
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)
available_logs = await dataManager.getDuelLogFromRecoverSearch(client.name_vpass)
......@@ -2110,28 +2221,21 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
code: 9
}
CLIENT_kick(client)
return
else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled
replay_id=info.pass.split("#")[1]
replay = await dataManager.getCloudReplayFromId(replay_id)
await client.open_cloud_replay(replay)
return
else if info.pass.toUpperCase()=="W" and settings.modules.cloud_replay.enabled
replay = await dataManager.getRandomCloudReplay()
await client.open_cloud_replay(replay)
else if info.version != settings.version and !settings.alternative_versions.includes(info.version)
ygopro.stoc_send_chat(client, (if info.version < settings.version then settings.modules.update else settings.modules.wait_update), ygopro.constants.COLORS.RED)
ygopro.stoc_send client, 'ERROR_MSG', {
msg: 4
code: settings.version
}
CLIENT_kick(client)
return
else if !await check_version()
return
else if !info.pass.length and !settings.modules.random_duel.enabled and !settings.modules.windbot.enabled and !settings.modules.challonge.enabled
ygopro.stoc_die(client, "${blank_room_name}")
return
else if settings.modules.mysql.enabled and await dataManager.checkBan("name", client.name) #账号被封
exactBan = await dataManager.checkBanWithNameAndIP(client.name, client.ip)
if !exactBan
......@@ -2139,23 +2243,17 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
await dataManager.banPlayer(exactBan)
log.warn("BANNED USER LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "${banned_user_login}")
return
else if settings.modules.mysql.enabled and await dataManager.checkBan("ip", client.ip) #IP被封
log.warn("BANNED IP LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "${banned_ip_login}")
return
else if info.pass.length and settings.modules.mycard.enabled and info.pass[0...3] != 'AI#'
ygopro.stoc_send_chat(client, '${loading_user_info}', ygopro.constants.COLORS.BABYBLUE)
if info.pass.length <= 8
ygopro.stoc_die(client, '${invalid_password_length}')
return
if info.version != settings.version and settings.alternative_versions.includes(info.version)
info.version = settings.version
struct = ygopro.structs.get("CTOS_JoinGame")
struct._setBuff(buffer)
struct.set("version", info.version)
buffer = struct.buffer
await polyfill_version()
buffer = Buffer.from(info.pass[0...8], 'base64')
......@@ -2390,33 +2488,28 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
else if !client.name or client.name==""
ygopro.stoc_die(client, "${bad_user_name}")
return
else if ROOM_connected_ip[client.ip] > 5
log.warn("MULTI LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "${too_much_connection}" + client.ip)
return
else if !settings.modules.tournament_mode.enabled and !settings.modules.challonge.enabled and badwordR.level3.test(client.name)
log.warn("BAD NAME LEVEL 3", client.name, client.ip)
ygopro.stoc_die(client, "${bad_name_level3}")
return
else if !settings.modules.tournament_mode.enabled and !settings.modules.challonge.enabled and badwordR.level2.test(client.name)
log.warn("BAD NAME LEVEL 2", client.name, client.ip)
ygopro.stoc_die(client, "${bad_name_level2}")
return
else if !settings.modules.tournament_mode.enabled and !settings.modules.challonge.enabled and badwordR.level1.test(client.name)
log.warn("BAD NAME LEVEL 1", client.name, client.ip)
ygopro.stoc_die(client, "${bad_name_level1}")
return
else if info.pass.length && !ROOM_validate(info.pass)
ygopro.stoc_die(client, "${invalid_password_room}")
return
else
if info.version != settings.version and settings.alternative_versions.includes(info.version)
info.version = settings.version
struct = ygopro.structs.get("CTOS_JoinGame")
struct._setBuff(buffer)
struct.set("version", info.version)
buffer = struct.buffer
await polyfill_version()
#log.info 'join_game',info.pass, client.name
room = await ROOM_find_or_create_by_name(info.pass, client.ip)
......@@ -2515,6 +2608,12 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
return unless room and !client.reconnecting
msg = buffer.readInt8(0)
msg_name = ygopro.constants.MSG[msg]
new_buf = await msg_polyfill.polyfillGameMsg(client.actual_version, msg_name, buffer)
if new_buf
buffer = new_buf
record_last_game_msg = () ->
client.last_game_msg = buffer
client.last_game_msg_title = msg_name
#console.log client.pos, "MSG", msg_name
if msg_name == 'RETRY' and room.recovering
room.finish_recover(true)
......@@ -2540,12 +2639,10 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
ygopro.stoc_send(client, 'GAME_MSG', client.last_game_msg)
return true
else
client.last_game_msg = buffer
client.last_game_msg_title = msg_name
record_last_game_msg()
# log.info(client.name, client.last_game_msg_title)
else if msg_name != 'RETRY'
client.last_game_msg = buffer
client.last_game_msg_title = msg_name
record_last_game_msg()
# log.info(client.name, client.last_game_msg_title)
if (msg >= 10 and msg < 30) or msg == 132 or (msg >= 140 and msg <= 144) #SELECT和ANNOUNCE开头的消息
......@@ -2790,7 +2887,10 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
room.recover_buffers[client.pos].push(buffer)
return true
await return false
if new_buf
return buffer
else
return false
#房间管理
ygopro.ctos_follow 'HS_TOOBSERVER', true, (buffer, info, client, server, datas)->
......@@ -3040,7 +3140,7 @@ ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
deck_arena = deck_arena + 'custom'
#log.info "DECK LOG START", client.name, room.arena
if settings.modules.deck_log.local
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + client.ip.slice(7) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_')
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + toIpv4(client.ip) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_')
fs.writeFile settings.modules.deck_log.local + deck_name + '.ydk', deck_text, 'utf-8', (err) ->
if err
log.warn 'DECK SAVE ERROR', err
......@@ -3283,13 +3383,13 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
CLIENT_kick_reconnect(client, buffer)
else
ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED)
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
})
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 2,
code: 0
})
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
})
return true
room=ROOM_all[client.rid]
return false unless room
......@@ -3315,17 +3415,20 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
CLIENT_kick(room.dueling_players[oppo_pos - win_pos])
CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]) if room.hostinfo.mode == 2
return true
struct = ygopro.structs.get("deck")
struct._setBuff(buffer)
deck_ok = (msg) ->
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.BABYBLUE)
await ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.BABYBLUE)
return false
deck_bad = (msg) ->
struct.set("mainc", 1)
struct.set("sidec", 1)
struct.set("deckbuf", [4392470, 4392470])
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.RED)
return false
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
})
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 2,
code: 0
})
return true
if room.random_type or room.arena
if client.pos == 0
room.waiting_for_player = room.waiting_for_player2
......@@ -3374,15 +3477,9 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
found_deck=deck
if found_deck
deck_text = await fs.promises.readFile(settings.modules.tournament_mode.deck_path+found_deck,{encoding:"ASCII"})
deck_array=deck_text.split(/\r?\n/)
deck_main=[]
deck_side=[]
current_deck=deck_main
for line in deck_array
if line.indexOf("!side")>=0
current_deck=deck_side
card=parseInt(line)
current_deck.push(card) unless isNaN(card) or line.endsWith("#")
deck_obj = YGOProDeck.fromYdkString(deck_text)
deck_main=deck_obj.main.concat(deck_obj.extra)
deck_side=deck_obj.side
if _.isEqual(buff_main, deck_main) and _.isEqual(buff_side, deck_side)
#log.info("deck ok: " + client.name)
return deck_ok("${deck_correct_part1} #{found_deck} ${deck_correct_part2}")
......@@ -3394,11 +3491,12 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
return deck_bad("#{client.name}${deck_not_found}")
await return false
ygopro.ctos_follow 'RESPONSE', false, (buffer, info, client, server, datas)->
ygopro.ctos_follow 'RESPONSE', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
return unless room and (room.random_type or room.arena)
if room and (room.random_type or room.arena)
room.refreshLastActiveTime()
await return
await msg_polyfill.polyfillResponse(client.actual_version, client.last_game_msg_title, buffer)
return false
ygopro.stoc_follow 'TIME_LIMIT', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid]
......@@ -3706,7 +3804,7 @@ if true
users: _.sortBy((for player in room.players when player.pos?
id: (-1).toString(),
name: player.name,
ip: if settings.modules.http.show_ip and pass_validated and !player.is_local then player.ip.slice(7) else null,
ip: if settings.modules.http.show_ip and pass_validated and !player.is_local then toIpv4(player.ip) else null,
status: if settings.modules.http.show_info and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and player.pos != 7 then (
score: room.scores[player.name_vpass],
lp: if player.lp? then player.lp else room.hostinfo.start_lp,
......@@ -3971,19 +4069,13 @@ if true
ip6addr = require('ip6addr')
neosRequestListener = (client, req) ->
physicalAddress = req.socket.remoteAddress
if settings.modules.neos.trusted_proxies.some((trusted) ->
cidr = if trusted.includes('/') then ip6addr.createCIDR(trusted) else ip6addr.createAddrRange(trusted, trusted)
return cidr.contains(physicalAddress)
)
ipHeader = req.headers[settings.modules.neos.trusted_proxy_header]
if ipHeader
client.ip = ipHeader.split(',')[0].trim()
if !client.ip
client.ip = physicalAddress
client.setTimeout = () -> true
client.destroy = () -> client.close()
client.isWs = true
client.physical_ip = req.socket.remoteAddress or ""
xff_ip = req.headers[settings.modules.neos.trusted_proxy_header]
if CLIENT_set_ip(client, xff_ip)
return
netRequestHandler(client)
......
// Generated by CoffeeScript 2.7.0
(function() {
// 标准库
var CLIENT_get_absolute_pos, CLIENT_get_authorize_key, CLIENT_get_kick_reconnect_target, CLIENT_get_partner, CLIENT_heartbeat_register, CLIENT_heartbeat_unregister, CLIENT_import_data, CLIENT_is_able_to_kick_reconnect, CLIENT_is_able_to_reconnect, CLIENT_is_banned_by_mc, CLIENT_is_player, CLIENT_kick, CLIENT_kick_reconnect, CLIENT_pre_reconnect, CLIENT_reconnect, CLIENT_reconnect_register, CLIENT_reconnect_unregister, CLIENT_send_pre_reconnect_info, CLIENT_send_reconnect_info, CLIENT_send_replays, CLIENT_send_replays_and_kick, Q, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_oppentlist, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, _, _async, addCallback, athleticChecker, auth, axios, badwordR, badwords, ban_user, bunyan, challonge, checkFileExists, createDirectoryIfNotExists, crypto, dataManager, deck_name_match, dialogues, disconnect_list, exec, execFile, extra_mode_list, fs, geoip, getDuelLogQueryFromQs, getSeedTimet, get_memory_usage, http, httpRequestListener, importOldConfig, import_datas, init, ip6addr, lflists, loadJSON, loadJSONAsync, loadLFList, loadRemoteData, load_dialogues, load_tips, log, long_resolve_cards, memory_usage, merge, moment, moment_long_ago_string, moment_now, moment_now_string, neosRequestListener, net, netRequestHandler, os, osu, path, qs, real_windbot_server_ip, release_disconnect, report_to_big_brother, request, roomlist, rooms_count, setting_change, setting_get, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, url, users_cache, util, utility, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib;
var Aragami, CLIENT_get_absolute_pos, CLIENT_get_authorize_key, CLIENT_get_kick_reconnect_target, CLIENT_get_partner, CLIENT_heartbeat_register, CLIENT_heartbeat_unregister, CLIENT_import_data, CLIENT_is_able_to_kick_reconnect, CLIENT_is_able_to_reconnect, CLIENT_is_banned_by_mc, CLIENT_is_player, CLIENT_kick, CLIENT_kick_reconnect, CLIENT_pre_reconnect, CLIENT_reconnect, CLIENT_reconnect_register, CLIENT_reconnect_unregister, CLIENT_send_pre_reconnect_info, CLIENT_send_reconnect_info, CLIENT_send_replays, CLIENT_send_replays_and_kick, CLIENT_set_ip, Q, ROOM_all, ROOM_bad_ip, ROOM_ban_player, ROOM_clear_disconnect, ROOM_connected_ip, ROOM_find_by_name, ROOM_find_by_pid, ROOM_find_by_port, ROOM_find_by_title, ROOM_find_or_create_ai, ROOM_find_or_create_by_name, ROOM_find_or_create_random, ROOM_kick, ROOM_player_flee, ROOM_player_get_score, ROOM_player_lose, ROOM_player_win, ROOM_players_oppentlist, ROOM_unwelcome, ROOM_validate, ReplayParser, ResolveData, Room, SERVER_clear_disconnect, SERVER_kick, SOCKET_flush_data, YGOProDeck, _, _async, addCallback, aragami, aragami_classes, athleticChecker, auth, axios, badwordR, badwords, ban_user, bunyan, challonge, checkFileExists, createDirectoryIfNotExists, crypto, dataManager, deck_name_match, dialogues, disconnect_list, exec, execFile, extra_mode_list, fs, geoip, getDuelLogQueryFromQs, getRealIp, get_memory_usage, http, httpRequestListener, importOldConfig, import_datas, init, ip6addr, isTrustedProxy, lflists, loadJSON, loadJSONAsync, loadLFList, loadRemoteData, load_dialogues, load_tips, log, long_resolve_cards, memory_usage, merge, moment, moment_long_ago_string, moment_now, moment_now_string, msg_polyfill, neosRequestListener, net, netRequestHandler, os, osu, path, qs, real_windbot_server_ip, release_disconnect, report_to_big_brother, request, roomlist, rooms_count, setting_change, setting_get, setting_save, settings, spawn, spawnSync, spawn_windbot, tips, toIpv4, toIpv6, url, users_cache, util, utility, wait_room_start, wait_room_start_arena, windbot_looplimit, windbot_process, windbots, ygopro, zlib;
net = require('net');
......@@ -73,7 +73,7 @@
}
});
import_datas = global.import_datas = ["abuse_count", "ban_mc", "vpass", "rag", "rid", "is_post_watcher", "retry_count", "name", "pass", "name_vpass", "is_first", "lp", "card_count", "is_host", "pos", "surrend_confirm", "kick_count", "deck_saved", "main", "side", "side_interval", "side_tcount", "selected_preduel", "last_game_msg", "last_game_msg_title", "last_hint_msg", "start_deckbuf", "challonge_info", "ready_trap", "join_time", "arena_quit_free", "replays_sent"];
import_datas = global.import_datas = ["abuse_count", "ban_mc", "vpass", "rag", "rid", "is_post_watcher", "retry_count", "name", "pass", "name_vpass", "is_first", "lp", "card_count", "is_host", "pos", "surrend_confirm", "kick_count", "deck_saved", "main", "side", "side_interval", "side_tcount", "selected_preduel", "last_game_msg", "last_game_msg_title", "last_hint_msg", "start_deckbuf", "challonge_info", "ready_trap", "join_time", "arena_quit_free", "replays_sent", "actual_version"];
merge = require('deepmerge');
......@@ -85,6 +85,16 @@
Q = require("q");
YGOProDeck = require('ygopro-deck-encode').default;
Aragami = require('aragami').Aragami;
aragami = global.aragami = new Aragami(); // we use memory mode only
aragami_classes = global.aragami_classes = require('./aragami-classes.js');
msg_polyfill = global.msg_polyfill = require('./msg-polyfill/index.js');
//heapdump = require 'heapdump'
checkFileExists = async(path) => {
var e;
......@@ -422,6 +432,11 @@
settings.modules.hide_name = "start";
imported = true;
}
if (settings.modules.neos.trusted_proxies) {
settings.modules.trusted_proxies = settings.modules.neos.trusted_proxies;
delete settings.modules.neos.trusted_proxies;
imported = true;
}
//finish
keysFromEnv = Object.keys(process.env).filter((key) => {
return key.startsWith('SRVPRO_');
......@@ -567,7 +582,7 @@
long_resolve_cards = global.long_resolve_cards = (await loadJSONAsync('./data/long_resolve_cards.json'));
}
if (settings.modules.tournament_mode.enable_recover) {
ReplayParser = global.ReplayParser = require("./Replay.js");
ReplayParser = global.ReplayParser = (require("./Replay.js")).Replay;
}
if (settings.modules.athletic_check.enabled) {
AthleticChecker = require("./athletic-check.js").AthleticChecker;
......@@ -1571,6 +1586,67 @@
CLIENT_kick(client);
};
toIpv4 = global.toIpv4 = function(ip) {
if (ip.startsWith('::ffff:')) {
return ip.slice(7);
}
return ip;
};
toIpv6 = global.toIpv6 = function(ip) {
if (/^(\d{1,3}\.){3}\d{1,3}$/.test(ip)) {
return '::ffff:' + ip;
}
return ip;
};
isTrustedProxy = global.isTrustedProxy = function(ip) {
return settings.modules.trusted_proxies.some(function(trusted) {
var cidr;
cidr = trusted.includes('/') ? ip6addr.createCIDR(trusted) : ip6addr.createAddrRange(trusted, trusted);
return cidr.contains(ip);
});
};
getRealIp = global.getRealIp = function(physical_ip, xff_ip) {
if (!xff_ip || xff_ip === physical_ip) {
return toIpv6(physical_ip);
}
if (isTrustedProxy(physical_ip)) {
return toIpv6(xff_ip.split(',')[0].trim());
}
log.warn(`Untrusted proxy detected: ${physical_ip} -> ${xff_ip}`);
return toIpv6(physical_ip);
};
CLIENT_set_ip = global.CLIENT_set_ip = function(client, xff_ip) {
var client_prev_ip, connect_count;
client_prev_ip = client.ip;
client.ip = getRealIp(client.physical_ip, xff_ip);
if (client_prev_ip === client.ip) {
return false;
}
if (client_prev_ip && ROOM_connected_ip[client_prev_ip] && ROOM_connected_ip[client_prev_ip] > 0) {
ROOM_connected_ip[client_prev_ip]--;
if (ROOM_connected_ip[client_prev_ip] <= 0) {
delete ROOM_connected_ip[client_prev_ip];
}
}
client.is_local = client.ip && (client.ip.includes('127.0.0.1') || client.ip.includes(real_windbot_server_ip));
connect_count = ROOM_connected_ip[client.ip] || 0;
if (!settings.modules.test_mode.no_connect_count_limit && !client.is_local && !isTrustedProxy(client.ip)) {
connect_count++;
}
ROOM_connected_ip[client.ip] = connect_count;
// log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
client.destroy();
return true;
}
return false;
};
SOCKET_flush_data = global.SOCKET_flush_data = async function(sk, datas) {
var buffer;
if (!sk || sk.isClosed) {
......@@ -1583,12 +1659,6 @@
return true;
};
getSeedTimet = global.getSeedTimet = function(count) {
return _.range(count).map(() => {
return 0;
});
};
Room = class Room {
constructor(name, hostinfo) {
var death_time, draw_count, duel_rule, extra_mode_func, lflist, param, rule, start_hand, start_lp, time_limit;
......@@ -1777,19 +1847,15 @@
}
spawn(firstSeed) {
var e, i, j, l, param, seeds;
var e, firstSeedBuf, i, j, param, ref;
param = [0, this.hostinfo.lflist, this.hostinfo.rule, this.hostinfo.mode, this.hostinfo.duel_rule, (this.hostinfo.no_check_deck ? 'T' : 'F'), (this.hostinfo.no_shuffle_deck ? 'T' : 'F'), this.hostinfo.start_lp, this.hostinfo.start_hand, this.hostinfo.draw_count, this.hostinfo.time_limit, this.hostinfo.replay_mode];
if (firstSeed) {
param.push(firstSeed);
seeds = getSeedTimet(2);
for (i = j = 0; j < 2; i = ++j) {
param.push(seeds[i]);
}
} else {
seeds = getSeedTimet(3);
for (i = l = 0; l < 3; i = ++l) {
param.push(seeds[i]);
// new replay with extended header and long seed
firstSeedBuf = Buffer.allocUnsafe(firstSeed.length * 4);
for (i = j = 0, ref = firstSeed.length; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
firstSeedBuf.writeUInt32LE(firstSeed[i], i * 4);
}
param.push(firstSeedBuf.toString('base64'));
}
try {
this.process = spawn('./ygopro', param, {
......@@ -1818,10 +1884,10 @@
this.port = parseInt(data);
_.each(this.players, (player) => {
player.server.connect(this.port, '127.0.0.1', async function() {
var buffer, len, m, ref;
ref = player.pre_establish_buffers;
for (m = 0, len = ref.length; m < len; m++) {
buffer = ref[m];
var buffer, l, len, ref1;
ref1 = player.pre_establish_buffers;
for (l = 0, len = ref1.length; l < len; l++) {
buffer = ref1[l];
await ygopro.helper.send(player.server, buffer);
}
player.established = true;
......@@ -2010,7 +2076,13 @@
}
try {
this.recover_replay = (await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replayFileName));
this.spawn(this.recover_replay.header.seed);
if (!this.recover_replay.header.seedSequence.length) {
// it's old replay, unsupported
log.warn("LOAD RECOVER REPLAY FAIL: Old replay format, unsupported", this.recover_duel_log.replayFileName);
this.terminate();
return false;
}
this.spawn(this.recover_replay.header.seedSequence);
return true;
} catch (error1) {
e = error1;
......@@ -2484,22 +2556,13 @@
// 网络连接
netRequestHandler = function(client) {
var closeHandler, connect_count, dataHandler, server;
var closeHandler, dataHandler, server;
if (!client.isWs) {
client.ip = client.remoteAddress || '';
}
client.is_local = client.ip && (client.ip.includes('127.0.0.1') || client.ip.includes(real_windbot_server_ip));
connect_count = ROOM_connected_ip[client.ip] || 0;
if (!settings.modules.test_mode.no_connect_count_limit && !client.is_local) {
connect_count++;
}
ROOM_connected_ip[client.ip] = connect_count;
//log.info "connect", client.ip, ROOM_connected_ip[client.ip]
if (ROOM_bad_ip[client.ip] > 5 || ROOM_connected_ip[client.ip] > 10) {
log.info('BAD IP', client.ip);
client.destroy();
client.physical_ip = client.remoteAddress || "";
if (CLIENT_set_ip(client)) {
return;
}
}
// server stand for the connection to ygopro server process
server = new net.Socket();
client.server = server;
......@@ -2508,7 +2571,7 @@
// 释放处理
closeHandler = function(error) {
var room;
var connect_count, room;
//log.info "client closed", client.name, error, client.isClosed
//log.info "disconnect", client.ip, ROOM_connected_ip[client.ip]
if (client.isClosed) {
......@@ -2516,10 +2579,14 @@
}
room = ROOM_all[client.rid];
connect_count = ROOM_connected_ip[client.ip];
if (connect_count > 0) {
if (connect_count && connect_count > 0) {
connect_count--;
}
if (connect_count === 0) {
delete ROOM_connected_ip[client.ip];
} else {
ROOM_connected_ip[client.ip] = connect_count;
}
}
client.isClosed = true;
if (settings.modules.heartbeat_detection.enabled) {
CLIENT_heartbeat_unregister(client);
......@@ -2649,9 +2716,8 @@
preconnect = false;
if (settings.modules.reconnect.enabled && client.pre_reconnecting_to_room) {
ctos_filter = ["UPDATE_DECK"];
}
if (client.name === null) {
ctos_filter = ["JOIN_GAME", "PLAYER_INFO"];
} else if (client.name === null) {
ctos_filter = ["EXTERNAL_ADDRESS", "JOIN_GAME", "PLAYER_INFO"];
preconnect = true;
}
handle_data = (await ygopro.helper.handleBuffer(ctos_buffer, "CTOS", ctos_filter, {
......@@ -2725,6 +2791,17 @@
// 功能模块
// return true to cancel a synchronous message
ygopro.ctos_follow('EXTERNAL_ADDRESS', true, function(buffer, info, client, server, datas) {
var ip_parts, ip_uint, xff_ip;
ip_uint = buffer.readUInt32BE(0);
if (ip_uint === 0) {
return false;
}
ip_parts = [(ip_uint >>> 24) & 0xFF, (ip_uint >>> 16) & 0xFF, (ip_uint >>> 8) & 0xFF, ip_uint & 0xFF];
xff_ip = ip_parts.join('.');
return CLIENT_set_ip(client, xff_ip);
});
ygopro.ctos_follow('PLAYER_INFO', true, async function(buffer, info, client, server, datas) {
var geo, lang, name, name_full, struct, vpass;
// second PLAYER_INFO = attack
......@@ -2780,7 +2857,55 @@
});
ygopro.ctos_follow('JOIN_GAME', true, async function(buffer, info, client, server, datas) {
var available_logs, check_buffer_indentity, create_room_name, create_room_with_action, decrypted_buffer, duelLog, e, exactBan, i, id, index, j, l, len, len1, len2, len3, m, matching_match, matching_participant, n, pre_room, recover_match, ref, ref1, replay, replay_id, replays, room, secret, struct, tournament_data, userData, userDataRes, userUrl;
var available_logs, check_buffer_indentity, check_version, create_room_name, create_room_with_action, decrypted_buffer, duelLog, e, exactBan, i, id, index, j, l, len, len1, len2, len3, m, matching_match, matching_participant, n, polyfill_version, pre_room, recover_match, ref, ref1, replay, replay_id, replays, room, secret, struct, tournament_data, userData, userDataRes, userUrl;
check_version = async function() {
var bad_version, blocker_obj, clean_blocker, client_key;
bad_version = function(msg) {
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.RED);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 4,
code: settings.version
});
CLIENT_kick(client);
return false;
};
client_key = CLIENT_get_authorize_key(client);
clean_blocker = function() {
return aragami.del(aragami_classes.ClientVersionBlocker, client_key);
};
if (info.version === settings.version) {
await clean_blocker();
return true;
}
if (settings.alternative_versions.includes(info.version)) {
if (!(await aragami.has(aragami_classes.ClientVersionBlocker, client_key))) {
blocker_obj = new aragami_classes.ClientVersionBlocker();
blocker_obj.clientKey = client_key;
await aragami.set(blocker_obj);
return bad_version("${version_to_polyfill}");
} else {
await clean_blocker();
return true;
}
}
return bad_version(info.version < settings.version ? settings.modules.update : settings.modules.wait_update);
};
polyfill_version = async function() {
var struct;
if (client.actual_version) {
return;
}
// already polyfilled
client.actual_version = info.version;
if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
info.version = settings.version;
struct = ygopro.structs.get("CTOS_JoinGame");
struct._setBuff(buffer);
struct.set("version", info.version);
buffer = struct.buffer;
ygopro.stoc_send_chat(client, "${version_polyfilled}", ygopro.constants.COLORS.BABYBLUE);
}
};
//log.info info
info.pass = info.pass.trim();
client.pass = info.pass;
......@@ -2789,8 +2914,10 @@
return;
} else if (settings.modules.stop) {
ygopro.stoc_die(client, settings.modules.stop);
return;
} else if (info.pass === "Marshtomp" || info.pass === "the Big Brother") {
ygopro.stoc_die(client, "${bad_user_name}");
return;
} else if (info.pass.toUpperCase() === "R" && settings.modules.cloud_replay.enabled) {
ygopro.stoc_send_chat(client, "${cloud_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
replays = (await dataManager.getCloudReplaysFromKey(CLIENT_get_authorize_key(client)));
......@@ -2803,6 +2930,7 @@
code: 9
});
CLIENT_kick(client);
return;
} else if (info.pass.toUpperCase() === "RC" && settings.modules.tournament_mode.enable_recover) {
ygopro.stoc_send_chat(client, "${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE);
available_logs = (await dataManager.getDuelLogFromRecoverSearch(client.name_vpass));
......@@ -2815,22 +2943,21 @@
code: 9
});
CLIENT_kick(client);
return;
} else if (info.pass.slice(0, 2).toUpperCase() === "R#" && settings.modules.cloud_replay.enabled) {
replay_id = info.pass.split("#")[1];
replay = (await dataManager.getCloudReplayFromId(replay_id));
await client.open_cloud_replay(replay);
return;
} else if (info.pass.toUpperCase() === "W" && settings.modules.cloud_replay.enabled) {
replay = (await dataManager.getRandomCloudReplay());
await client.open_cloud_replay(replay);
} else if (info.version !== settings.version && !settings.alternative_versions.includes(info.version)) {
ygopro.stoc_send_chat(client, (info.version < settings.version ? settings.modules.update : settings.modules.wait_update), ygopro.constants.COLORS.RED);
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 4,
code: settings.version
});
CLIENT_kick(client);
return;
} else if (!(await check_version())) {
return;
} else if (!info.pass.length && !settings.modules.random_duel.enabled && !settings.modules.windbot.enabled && !settings.modules.challonge.enabled) {
ygopro.stoc_die(client, "${blank_room_name}");
return;
} else if (settings.modules.mysql.enabled && (await dataManager.checkBan("name", client.name))) { //账号被封
exactBan = (await dataManager.checkBanWithNameAndIP(client.name, client.ip));
if (!exactBan) {
......@@ -2839,22 +2966,18 @@
}
log.warn("BANNED USER LOGIN", client.name, client.ip);
ygopro.stoc_die(client, "${banned_user_login}");
return;
} else if (settings.modules.mysql.enabled && (await dataManager.checkBan("ip", client.ip))) { //IP被封
log.warn("BANNED IP LOGIN", client.name, client.ip);
ygopro.stoc_die(client, "${banned_ip_login}");
return;
} else if (info.pass.length && settings.modules.mycard.enabled && info.pass.slice(0, 3) !== 'AI#') {
ygopro.stoc_send_chat(client, '${loading_user_info}', ygopro.constants.COLORS.BABYBLUE);
if (info.pass.length <= 8) {
ygopro.stoc_die(client, '${invalid_password_length}');
return;
}
if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
info.version = settings.version;
struct = ygopro.structs.get("CTOS_JoinGame");
struct._setBuff(buffer);
struct.set("version", info.version);
buffer = struct.buffer;
}
await polyfill_version();
buffer = Buffer.from(info.pass.slice(0, 8), 'base64');
if (buffer.length !== 6) {
ygopro.stoc_die(client, '${invalid_password_payload}');
......@@ -3149,28 +3272,28 @@
}
} else if (!client.name || client.name === "") {
ygopro.stoc_die(client, "${bad_user_name}");
return;
} else if (ROOM_connected_ip[client.ip] > 5) {
log.warn("MULTI LOGIN", client.name, client.ip);
ygopro.stoc_die(client, "${too_much_connection}" + client.ip);
return;
} else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && badwordR.level3.test(client.name)) {
log.warn("BAD NAME LEVEL 3", client.name, client.ip);
ygopro.stoc_die(client, "${bad_name_level3}");
return;
} else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && badwordR.level2.test(client.name)) {
log.warn("BAD NAME LEVEL 2", client.name, client.ip);
ygopro.stoc_die(client, "${bad_name_level2}");
return;
} else if (!settings.modules.tournament_mode.enabled && !settings.modules.challonge.enabled && badwordR.level1.test(client.name)) {
log.warn("BAD NAME LEVEL 1", client.name, client.ip);
ygopro.stoc_die(client, "${bad_name_level1}");
return;
} else if (info.pass.length && !ROOM_validate(info.pass)) {
ygopro.stoc_die(client, "${invalid_password_room}");
return;
} else {
if (info.version !== settings.version && settings.alternative_versions.includes(info.version)) {
info.version = settings.version;
struct = ygopro.structs.get("CTOS_JoinGame");
struct._setBuff(buffer);
struct.set("version", info.version);
buffer = struct.buffer;
}
await polyfill_version();
//log.info 'join_game',info.pass, client.name
room = (await ROOM_find_or_create_by_name(info.pass, client.ip));
if (!room) {
......@@ -3294,13 +3417,21 @@
};
ygopro.stoc_follow('GAME_MSG', true, async function(buffer, info, client, server, datas) {
var card, chain, check, count, cpos, deck_found, found, hint_type, i, id, j, l, len, len1, len2, len3, limbo_found, line, loc, m, max_loop, msg, msg_name, n, o, oppo_pos, phase, player, playertype, pos, ppos, reason, ref, ref1, ref2, ref3, ref4, ref5, room, trigger_location, val, win_pos;
var card, chain, check, count, cpos, deck_found, found, hint_type, i, id, j, l, len, len1, len2, len3, limbo_found, line, loc, m, max_loop, msg, msg_name, n, new_buf, o, oppo_pos, phase, player, playertype, pos, ppos, reason, record_last_game_msg, ref, ref1, ref2, ref3, ref4, ref5, room, trigger_location, val, win_pos;
room = ROOM_all[client.rid];
if (!(room && !client.reconnecting)) {
return;
}
msg = buffer.readInt8(0);
msg_name = ygopro.constants.MSG[msg];
new_buf = (await msg_polyfill.polyfillGameMsg(client.actual_version, msg_name, buffer));
if (new_buf) {
buffer = new_buf;
}
record_last_game_msg = function() {
client.last_game_msg = buffer;
return client.last_game_msg_title = msg_name;
};
//console.log client.pos, "MSG", msg_name
if (msg_name === 'RETRY' && room.recovering) {
room.finish_recover(true);
......@@ -3332,13 +3463,11 @@
return true;
}
} else {
client.last_game_msg = buffer;
client.last_game_msg_title = msg_name;
record_last_game_msg();
}
// log.info(client.name, client.last_game_msg_title)
} else if (msg_name !== 'RETRY') {
client.last_game_msg = buffer;
client.last_game_msg_title = msg_name;
record_last_game_msg();
}
// log.info(client.name, client.last_game_msg_title)
if ((msg >= 10 && msg < 30) || msg === 132 || (msg >= 140 && msg <= 144)) { //SELECT和ANNOUNCE开头的消息
......@@ -3675,7 +3804,11 @@
}
return true;
}
if (new_buf) {
return buffer;
} else {
return false;
}
});
//房间管理
......@@ -4055,7 +4188,7 @@
}
//log.info "DECK LOG START", client.name, room.arena
if (settings.modules.deck_log.local) {
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + client.ip.slice(7) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_');
deck_name = moment_now.format('YYYY-MM-DD HH-mm-ss') + ' ' + room.process_pid + ' ' + client.pos + ' ' + toIpv4(client.ip) + ' ' + client.name.replace(/[\/\\\?\*]/g, '_');
fs.writeFile(settings.modules.deck_log.local + deck_name + '.ydk', deck_text, 'utf-8', function(err) {
if (err) {
return log.warn('DECK SAVE ERROR', err);
......@@ -4377,7 +4510,7 @@
});
ygopro.ctos_follow('UPDATE_DECK', true, async function(buffer, info, client, server, datas) {
var athleticCheckResult, buff_main, buff_side, card, current_deck, deck, deck_array, deck_bad, deck_main, deck_ok, deck_side, deck_text, deckbuf_from_challonge, decks, found_deck, i, j, l, len, len1, line, oppo_pos, recover_player_data, recoveredDeck, room, struct, trim_deckbuf, win_pos;
var athleticCheckResult, buff_main, buff_side, deck, deck_bad, deck_main, deck_obj, deck_ok, deck_side, deck_text, deckbuf_from_challonge, decks, found_deck, i, j, len, oppo_pos, recover_player_data, recoveredDeck, room, trim_deckbuf, win_pos;
if (settings.modules.reconnect.enabled && client.pre_reconnecting) {
if (!CLIENT_is_able_to_reconnect(client) && !CLIENT_is_able_to_kick_reconnect(client)) {
ygopro.stoc_send_chat(client, "${reconnect_failed}", ygopro.constants.COLORS.RED);
......@@ -4388,13 +4521,13 @@
CLIENT_kick_reconnect(client, buffer);
} else {
ygopro.stoc_send_chat(client, "${deck_incorrect_reconnect}", ygopro.constants.COLORS.RED);
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
});
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 2,
code: 0
});
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
});
}
return true;
}
......@@ -4447,18 +4580,22 @@
}
return true;
}
struct = ygopro.structs.get("deck");
struct._setBuff(buffer);
deck_ok = function(msg) {
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.BABYBLUE);
deck_ok = async function(msg) {
await ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.BABYBLUE);
return false;
};
deck_bad = function(msg) {
struct.set("mainc", 1);
struct.set("sidec", 1);
struct.set("deckbuf", [4392470, 4392470]);
ygopro.stoc_send_chat(client, msg, ygopro.constants.COLORS.RED);
return false;
if (room.duel_stage === ygopro.constants.DUEL_STAGE.BEGIN) {
ygopro.stoc_send(client, 'HS_PLAYER_CHANGE', {
status: (client.pos << 4) | 0xa
});
}
ygopro.stoc_send(client, 'ERROR_MSG', {
msg: 2,
code: 0
});
return true;
};
if (room.random_type || room.arena) {
if (client.pos === 0) {
......@@ -4526,20 +4663,9 @@
deck_text = (await fs.promises.readFile(settings.modules.tournament_mode.deck_path + found_deck, {
encoding: "ASCII"
}));
deck_array = deck_text.split(/\r?\n/);
deck_main = [];
deck_side = [];
current_deck = deck_main;
for (l = 0, len1 = deck_array.length; l < len1; l++) {
line = deck_array[l];
if (line.indexOf("!side") >= 0) {
current_deck = deck_side;
}
card = parseInt(line);
if (!(isNaN(card) || line.endsWith("#"))) {
current_deck.push(card);
}
}
deck_obj = YGOProDeck.fromYdkString(deck_text);
deck_main = deck_obj.main.concat(deck_obj.extra);
deck_side = deck_obj.side;
if (_.isEqual(buff_main, deck_main) && _.isEqual(buff_side, deck_side)) {
//log.info("deck ok: " + client.name)
return deck_ok(`\${deck_correct_part1} ${found_deck} \${deck_correct_part2}`);
......@@ -4558,13 +4684,14 @@
return false;
});
ygopro.ctos_follow('RESPONSE', false, async function(buffer, info, client, server, datas) {
ygopro.ctos_follow('RESPONSE', true, async function(buffer, info, client, server, datas) {
var room;
room = ROOM_all[client.rid];
if (!(room && (room.random_type || room.arena))) {
return;
}
if (room && (room.random_type || room.arena)) {
room.refreshLastActiveTime();
}
await msg_polyfill.polyfillResponse(client.actual_version, client.last_game_msg_title, buffer);
return false;
});
ygopro.stoc_follow('TIME_LIMIT', true, async function(buffer, info, client, server, datas) {
......@@ -4990,7 +5117,7 @@
results.push({
id: (-1).toString(),
name: player.name,
ip: settings.modules.http.show_ip && pass_validated && !player.is_local ? player.ip.slice(7) : null,
ip: settings.modules.http.show_ip && pass_validated && !player.is_local ? toIpv4(player.ip) : null,
status: settings.modules.http.show_info && room.duel_stage !== ygopro.constants.DUEL_STAGE.BEGIN && player.pos !== 7 ? {
score: room.scores[player.name_vpass],
lp: player.lp != null ? player.lp : room.hostinfo.start_lp,
......@@ -5295,21 +5422,7 @@
ip6addr = require('ip6addr');
neosRequestListener = function(client, req) {
var ipHeader, physicalAddress;
physicalAddress = req.socket.remoteAddress;
if (settings.modules.neos.trusted_proxies.some(function(trusted) {
var cidr;
cidr = trusted.includes('/') ? ip6addr.createCIDR(trusted) : ip6addr.createAddrRange(trusted, trusted);
return cidr.contains(physicalAddress);
})) {
ipHeader = req.headers[settings.modules.neos.trusted_proxy_header];
if (ipHeader) {
client.ip = ipHeader.split(',')[0].trim();
}
}
if (!client.ip) {
client.ip = physicalAddress;
}
var xff_ip;
client.setTimeout = function() {
return true;
};
......@@ -5317,6 +5430,11 @@
return client.close();
};
client.isWs = true;
client.physical_ip = req.socket.remoteAddress || "";
xff_ip = req.headers[settings.modules.neos.trusted_proxy_header];
if (CLIENT_set_ip(client, xff_ip)) {
return;
}
return netRequestHandler(client);
};
......
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