Commit 3c3e3381 authored by nanahira's avatar nanahira

use Typescript YGOProMessages module

parent dc14a333
This diff is collapsed.
import { Struct } from "./struct";
import fs from "fs";
import _ from "underscore";
import structs_declaration from "./data/structs.json";
import typedefs from "./data/typedefs.json";
import proto_structs from "./data/proto_structs.json";
import constants from "./data/constants.json";
import net from "net";
class Handler {
handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean>;
synchronous: boolean;
constructor(handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean>, synchronous: boolean) {
this.handler = handler;
this.synchronous = synchronous || false;
}
async handle(buffer: Buffer, info: any, datas: Buffer[], params: any) {
if (this.synchronous) {
return !!(await this.handler(buffer, info, datas, params));
} else {
const newBuffer = Buffer.from(buffer);
const newDatas = datas.map(b => Buffer.from(b));
this.handler(newBuffer, info, newDatas, params);
return false;
}
}
}
interface HandlerList {
STOC: Map<number, Handler[]>[];
CTOS: Map<number, Handler[]>[];
}
interface DirectionAndProto {
direction: string;
proto: string;
}
export interface Feedback{
type: string;
message: string;
}
export interface HandleResult {
datas: Buffer[];
feedback: Feedback;
}
export class YGOProMessagesHelper {
handlers: HandlerList;
structs: Map<string, Struct>;
structs_declaration: any;
typedefs: any;
proto_structs: any;
constants: any;
constructor() {
this.handlers = {
STOC: [new Map(),
new Map(),
new Map(),
new Map(),
new Map(),
],
CTOS: [new Map(),
new Map(),
new Map(),
new Map(),
new Map(),
]
}
this.initDatas();
this.initStructs();
}
initDatas() {
this.structs_declaration = structs_declaration;
this.typedefs = typedefs;
this.proto_structs = proto_structs;
this.constants = constants;
}
initStructs() {
this.structs = new Map();
for (let name in this.structs_declaration) {
const declaration = this.structs_declaration[name];
let result = Struct();
for (let field of declaration) {
if (field.encoding) {
switch (field.encoding) {
case "UTF-16LE":
result.chars(field.name, field.length * 2, field.encoding);
break;
default:
throw `unsupported encoding: ${field.encoding}`;
}
} else {
let type = field.type;
if (this.typedefs[type]) {
type = this.typedefs[type];
}
if (field.length) {
result.array(field.name, field.length, type); //不支持结构体
} else {
if (this.structs.has(type)) {
result.struct(field.name, this.structs.get(type));
} else {
result[type](field.name);
}
}
}
}
this.structs.set(name, result);
}
}
getDirectionAndProto(protoStr: string): DirectionAndProto {
const protoStrMatch = protoStr.match(/^(STOC|CTOS)_([_A-Z]+)$/);
if (!protoStrMatch) {
throw `Invalid proto string: ${protoStr}`
}
return {
direction: protoStrMatch[1].toUpperCase(),
proto: protoStrMatch[2].toUpperCase()
}
}
translateProto(proto: string | number, direction: string): number {
const directionProtoList = this.constants[direction];
if (typeof proto !== "string") {
return proto;
}
const translatedProto = _.find(Object.keys(directionProtoList), p => {
return directionProtoList[p] === proto;
});
if (!translatedProto) {
throw `unknown proto ${direction} ${proto}`;
}
return parseInt(translatedProto);
}
sendMessage(socket: net.Socket, protostr: string, info: string | Buffer | any) {
const {
direction,
proto
} = this.getDirectionAndProto(protostr);
let buffer: string | Buffer;
if (!socket.remoteAddress) {
return;
}
//console.log(proto, this.proto_structs[direction][proto]);
//const directionProtoList = this.constants[direction];
if (typeof info === 'undefined') {
buffer = "";
} else if (Buffer.isBuffer(info)) {
buffer = info;
} else {
let struct = this.structs.get(this.proto_structs[direction][proto]);
struct.allocate();
struct.set(info);
buffer = struct.buffer();
}
const translatedProto = this.translateProto(proto, direction);
let header = Buffer.allocUnsafe(3);
header.writeUInt16LE(buffer.length + 1, 0);
header.writeUInt8(translatedProto, 2);
socket.write(header);
if (buffer.length) {
socket.write(buffer);
}
}
addHandler(protostr: string, handler: (buffer: Buffer, info: any, datas: Buffer[], params: any) => Promise<boolean>, synchronous: boolean, priority: number) {
if (priority < 0 || priority > 4) {
throw "Invalid priority: " + priority;
}
let {
direction,
proto
} = this.getDirectionAndProto(protostr);
synchronous = synchronous || false;
priority = priority || 1;
const handlerObj = new Handler(handler, synchronous);
let handlerCollection: Map<number, Handler[]> = this.handlers[direction][priority];
const translatedProto = this.translateProto(proto, direction);
if (!handlerCollection.has(translatedProto)) {
handlerCollection.set(translatedProto, []);
}
handlerCollection.get(translatedProto).push(handlerObj);
}
async handleBuffer(messageBuffer: Buffer, direction: string, protoFilter: string[], params: any): Promise<HandleResult> {
let feedback: Feedback = null;
let messageLength = 0;
let bufferProto = 0;
let datas: Buffer[] = [];
for (let l = 0; l < 1000; ++l) {
if (messageLength === 0) {
if (messageBuffer.length >= 2) {
messageLength = messageBuffer.readUInt16LE(0);
} else {
if (messageBuffer.length !== 0) {
feedback = {
type: "BUFFER_LENGTH",
message: `Bad ${direction} buffer length`
};
}
break;
}
} else if (bufferProto === 0) {
if (messageBuffer.length >= 3) {
bufferProto = messageBuffer.readUInt8(2);
} else {
feedback = {
type: "PROTO_LENGTH",
message: `Bad ${direction} proto length`
};
break;
}
} else {
if (messageBuffer.length >= 2 + messageLength) {
const proto = this.constants[direction][bufferProto];
let cancel = proto && protoFilter && _.indexOf(protoFilter, proto) === -1;
let buffer = messageBuffer.slice(3, 2 + messageLength);
//console.log(l, direction, proto, cancel);
for (let priority = 0; priority < 4; ++priority) {
if (cancel) {
break;
}
const handlerCollection: Map<number, Handler[]> = this.handlers[direction][priority];
if (proto && handlerCollection.has(bufferProto)) {
let struct = this.structs.get(this.proto_structs[direction][proto]);
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) {
break;
}
}
}
}
if (!cancel) {
datas.push(messageBuffer.slice(0, 2 + messageLength));
}
messageBuffer = messageBuffer.slice(2 + messageLength);
messageLength = 0;
bufferProto = 0;
} else {
if (direction === "STOC" || messageLength !== 17735) {
feedback = {
type: "MESSAGE_LENGTH",
message: `Bad ${direction} message length`
};
}
break;
}
}
if (l === 999) {
feedback = {
type: "OVERSIZE",
message: `Oversized ${direction}`
};
}
}
return {
datas,
feedback
};
}
}
...@@ -27,6 +27,16 @@ ...@@ -27,6 +27,16 @@
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
}, },
"@types/node": {
"version": "14.0.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz",
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA=="
},
"@types/underscore": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.0.tgz",
"integrity": "sha512-ZAbqul7QAKpM2h1PFGa5ETN27ulmqtj0QviYHasw9LffvXZvVHuraOx/FOsIPPDNGZN0Qo1nASxxSfMYOtSoCw=="
},
"abbrev": { "abbrev": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
], ],
"author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>, Nanahira <78877@qq.com>", "author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>, Nanahira <78877@qq.com>",
"dependencies": { "dependencies": {
"@types/node": "^14.0.13",
"@types/underscore": "^1.10.0",
"async": "^3.2.0", "async": "^3.2.0",
"axios": "^0.19.2", "axios": "^0.19.2",
"bunyan": "latest", "bunyan": "latest",
......
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"esModuleInterop": true,
"resolveJsonModule": true,
"sourceMap": true
},
"compileOnSave": true,
"allowJs": true,
"include": [
"*.ts"
]
}
...@@ -1836,7 +1836,7 @@ ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)-> ...@@ -1836,7 +1836,7 @@ ygopro.ctos_follow 'PLAYER_INFO', true, (buffer, info, client, server, datas)->
log.warn "ban get bad json", banMCRequest.data log.warn "ban get bad json", banMCRequest.data
catch e catch e
log.warn 'ban get error', e.toString() log.warn 'ban get error', e.toString()
struct = ygopro.structs["CTOS_PlayerInfo"] struct = ygopro.structs.get("CTOS_PlayerInfo")
struct._setBuff(buffer) struct._setBuff(buffer)
struct.set("name", name) struct.set("name", name)
buffer = struct.buffer buffer = struct.buffer
...@@ -1953,7 +1953,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -1953,7 +1953,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
#if info.version >= 9020 and settings.version == 4927 #强行兼容23333版 #if info.version >= 9020 and settings.version == 4927 #强行兼容23333版
# info.version = settings.version # info.version = settings.version
# struct = ygopro.structs["CTOS_JoinGame"] # struct = ygopro.structs.get("CTOS_JoinGame")
# struct._setBuff(buffer) # struct._setBuff(buffer)
# struct.set("version", info.version) # struct.set("version", info.version)
# buffer = struct.buffer # buffer = struct.buffer
...@@ -2314,7 +2314,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2314,7 +2314,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
else else
#if info.version >= 9020 and settings.version == 4927 #强行兼容23333版 #if info.version >= 9020 and settings.version == 4927 #强行兼容23333版
# info.version = settings.version # info.version = settings.version
# struct = ygopro.structs["CTOS_JoinGame"] # struct = ygopro.structs.get("CTOS_JoinGame")
# struct._setBuff(buffer) # struct._setBuff(buffer)
# struct.set("version", info.version) # struct.set("version", info.version)
# buffer = struct.buffer # buffer = struct.buffer
...@@ -2787,7 +2787,7 @@ ygopro.stoc_follow 'HS_PLAYER_ENTER', true, (buffer, info, client, server, datas ...@@ -2787,7 +2787,7 @@ ygopro.stoc_follow 'HS_PLAYER_ENTER', true, (buffer, info, client, server, datas
return false unless room and settings.modules.hide_name and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN return false unless room and settings.modules.hide_name and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
pos = info.pos pos = info.pos
if pos < 4 and pos != client.pos if pos < 4 and pos != client.pos
struct = ygopro.structs["STOC_HS_PlayerEnter"] struct = ygopro.structs.get("STOC_HS_PlayerEnter")
struct._setBuff(buffer) struct._setBuff(buffer)
struct.set("name", "********") struct.set("name", "********")
buffer = struct.buffer buffer = struct.buffer
...@@ -3178,7 +3178,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)-> ...@@ -3178,7 +3178,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
report_to_big_brother room.name, client.name, client.ip, 1, oldmsg, RegExp.$1 report_to_big_brother room.name, client.name, client.ip, 1, oldmsg, RegExp.$1
client.abuse_count=client.abuse_count+1 client.abuse_count=client.abuse_count+1
ygopro.stoc_send_chat(client, "${chat_warn_level1}") ygopro.stoc_send_chat(client, "${chat_warn_level1}")
struct = ygopro.structs["chat"] struct = ygopro.structs.get("chat")
struct._setBuff(buffer) struct._setBuff(buffer)
struct.set("msg", msg) struct.set("msg", msg)
buffer = struct.buffer buffer = struct.buffer
...@@ -3238,7 +3238,7 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)-> ...@@ -3238,7 +3238,7 @@ 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])
CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]) if room.hostinfo.mode == 2 CLIENT_kick(room.dueling_players[oppo_pos - win_pos + 1]) if room.hostinfo.mode == 2
return true return true
struct = ygopro.structs["deck"] struct = ygopro.structs.get("deck")
struct._setBuff(buffer) struct._setBuff(buffer)
if room.random_type or room.arena if room.random_type or room.arena
if client.pos == 0 if client.pos == 0
......
...@@ -2475,7 +2475,7 @@ ...@@ -2475,7 +2475,7 @@
log.warn('ban get error', e.toString()); log.warn('ban get error', e.toString());
} }
} }
struct = ygopro.structs["CTOS_PlayerInfo"]; struct = ygopro.structs.get("CTOS_PlayerInfo");
struct._setBuff(buffer); struct._setBuff(buffer);
struct.set("name", name); struct.set("name", name);
buffer = struct.buffer; buffer = struct.buffer;
...@@ -2594,7 +2594,7 @@ ...@@ -2594,7 +2594,7 @@
} }
//if info.version >= 9020 and settings.version == 4927 #强行兼容23333版 //if info.version >= 9020 and settings.version == 4927 #强行兼容23333版
// info.version = settings.version // info.version = settings.version
// struct = ygopro.structs["CTOS_JoinGame"] // struct = ygopro.structs.get("CTOS_JoinGame")
// struct._setBuff(buffer) // struct._setBuff(buffer)
// struct.set("version", info.version) // struct.set("version", info.version)
// buffer = struct.buffer // buffer = struct.buffer
...@@ -3022,7 +3022,7 @@ ...@@ -3022,7 +3022,7 @@
} else { } else {
//if info.version >= 9020 and settings.version == 4927 #强行兼容23333版 //if info.version >= 9020 and settings.version == 4927 #强行兼容23333版
// info.version = settings.version // info.version = settings.version
// struct = ygopro.structs["CTOS_JoinGame"] // struct = ygopro.structs.get("CTOS_JoinGame")
// struct._setBuff(buffer) // struct._setBuff(buffer)
// struct.set("version", info.version) // struct.set("version", info.version)
// buffer = struct.buffer // buffer = struct.buffer
...@@ -3652,7 +3652,7 @@ ...@@ -3652,7 +3652,7 @@
} }
pos = info.pos; pos = info.pos;
if (pos < 4 && pos !== client.pos) { if (pos < 4 && pos !== client.pos) {
struct = ygopro.structs["STOC_HS_PlayerEnter"]; struct = ygopro.structs.get("STOC_HS_PlayerEnter");
struct._setBuff(buffer); struct._setBuff(buffer);
struct.set("name", "********"); struct.set("name", "********");
buffer = struct.buffer; buffer = struct.buffer;
...@@ -4222,7 +4222,7 @@ ...@@ -4222,7 +4222,7 @@
report_to_big_brother(room.name, client.name, client.ip, 1, oldmsg, RegExp.$1); report_to_big_brother(room.name, client.name, client.ip, 1, oldmsg, RegExp.$1);
client.abuse_count = client.abuse_count + 1; client.abuse_count = client.abuse_count + 1;
ygopro.stoc_send_chat(client, "${chat_warn_level1}"); ygopro.stoc_send_chat(client, "${chat_warn_level1}");
struct = ygopro.structs["chat"]; struct = ygopro.structs.get("chat");
struct._setBuff(buffer); struct._setBuff(buffer);
struct.set("msg", msg); struct.set("msg", msg);
buffer = struct.buffer; buffer = struct.buffer;
...@@ -4316,7 +4316,7 @@ ...@@ -4316,7 +4316,7 @@
} }
return true; return true;
} }
struct = ygopro.structs["deck"]; struct = ygopro.structs.get("deck");
struct._setBuff(buffer); struct._setBuff(buffer);
if (room.random_type || room.arena) { if (room.random_type || room.arena) {
if (client.pos === 0) { if (client.pos === 0) {
......
...@@ -7,8 +7,8 @@ loadJSON = require('load-json-file').sync ...@@ -7,8 +7,8 @@ loadJSON = require('load-json-file').sync
@i18ns = loadJSON './data/i18n.json' @i18ns = loadJSON './data/i18n.json'
YGOProMessageHelper = require("./YGOProMessages.js") # 为 SRVPro2 准备的库,这里拿这个库只用来测试,SRVPro1 对异步支持不是特别完善,因此不会有很多异步优化 YGOProMessagesHelper = require("./YGOProMessages.js").YGOProMessagesHelper # 为 SRVPro2 准备的库,这里拿这个库只用来测试,SRVPro1 对异步支持不是特别完善,因此不会有很多异步优化
@helper = new YGOProMessageHelper() @helper = new YGOProMessagesHelper()
@structs = @helper.structs @structs = @helper.structs
@structs_declaration = @helper.structs_declaration @structs_declaration = @helper.structs_declaration
......
// Generated by CoffeeScript 2.5.1 // Generated by CoffeeScript 2.5.1
(function() { (function() {
var Struct, YGOProMessageHelper, _, loadJSON, translateHandler; var Struct, YGOProMessagesHelper, _, loadJSON, translateHandler;
_ = require('underscore'); _ = require('underscore');
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
this.i18ns = loadJSON('./data/i18n.json'); this.i18ns = loadJSON('./data/i18n.json');
YGOProMessageHelper = require("./YGOProMessages.js"); // 为 SRVPro2 准备的库,这里拿这个库只用来测试,SRVPro1 对异步支持不是特别完善,因此不会有很多异步优化 YGOProMessagesHelper = require("./YGOProMessages.js").YGOProMessagesHelper; // 为 SRVPro2 准备的库,这里拿这个库只用来测试,SRVPro1 对异步支持不是特别完善,因此不会有很多异步优化
this.helper = new YGOProMessageHelper(); this.helper = new YGOProMessagesHelper();
this.structs = this.helper.structs; this.structs = this.helper.structs;
......
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