Commit ac5426de authored by nanahira's avatar nanahira

use buffer mutate for polyfill

parent c7fc8f65
......@@ -223,8 +223,8 @@ class YGOProMessagesHelper {
break;
}
let buffer = messageBuffer.slice(3, 2 + messageLength);
let bufferMutated = false;
//console.log(l, direction, proto, cancel);
let shrinkCount = 0;
for (let priority = 0; priority < 4; ++priority) {
if (cancel) {
break;
......@@ -232,15 +232,20 @@ class YGOProMessagesHelper {
const handlerCollection = 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 = underscore_1.default.clone(struct.fields);
}
for (let handler of handlerCollection.get(bufferProto)) {
for (const handler of handlerCollection.get(bufferProto)) {
let info = null;
if (struct) {
struct._setBuff(buffer);
info = underscore_1.default.clone(struct.fields);
}
cancel = await handler.handle(buffer, info, datas, params);
if (cancel) {
if (typeof cancel === "string") {
if (Buffer.isBuffer(cancel)) {
buffer = cancel;
bufferMutated = true;
cancel = false;
}
else if (typeof cancel === "string") {
if (cancel === '_cancel') {
return {
datas: [],
......@@ -248,8 +253,15 @@ class YGOProMessagesHelper {
};
}
else if (cancel.startsWith('_shrink_')) {
shrinkCount += parseInt(cancel.slice(8));
cancel = false;
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;
......@@ -257,14 +269,15 @@ class YGOProMessagesHelper {
}
}
}
if (!cancel && shrinkCount <= messageLength) {
if (shrinkCount > 0) {
// rewrite first 2 bytes of length
const oldLength = messageBuffer.readUInt16LE(0);
const newLength = oldLength - shrinkCount;
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));
}
datas.push(messageBuffer.slice(0, 2 + messageLength - shrinkCount));
}
messageBuffer = messageBuffer.slice(2 + messageLength);
messageLength = 0;
......
......@@ -8,13 +8,13 @@ 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);
} else {
......@@ -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,8 +273,8 @@ export class YGOProMessagesHelper {
break;
}
let buffer = messageBuffer.slice(3, 2 + messageLength);
let bufferMutated = false;
//console.log(l, direction, proto, cancel);
let shrinkCount = 0;
for (let priority = 0; priority < 4; ++priority) {
if (cancel) {
break;
......@@ -282,23 +282,33 @@ 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]);
let info = null;
if (struct) {
struct._setBuff(buffer);
info = _.clone(struct.fields);
}
for (let handler of handlerCollection.get(bufferProto)) {
for (const handler of handlerCollection.get(bufferProto)) {
let info = null;
if (struct) {
struct._setBuff(buffer);
info = _.clone(struct.fields);
}
cancel = await handler.handle(buffer, info, datas, params);
if (cancel) {
if (typeof cancel === "string") {
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_')) {
shrinkCount += parseInt(cancel.slice(8));
cancel = false;
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;
......@@ -306,14 +316,14 @@ export class YGOProMessagesHelper {
}
}
}
if (!cancel && shrinkCount <= messageLength) {
if (shrinkCount > 0) {
// rewrite first 2 bytes of length
const oldLength = messageBuffer.readUInt16LE(0);
const newLength = oldLength - shrinkCount;
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));
}
datas.push(messageBuffer.slice(0, 2 + messageLength - shrinkCount));
}
messageBuffer = messageBuffer.slice(2 + messageLength);
messageLength = 0;
......
......@@ -2,33 +2,34 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasePolyfiller = void 0;
class BasePolyfiller {
constructor() {
this.shrinkCount = 0;
async polyfillGameMsg(msgTitle, buffer) {
return;
}
async polyfillResponse(msgTitle, buffer) {
return;
}
async polyfillGameMsg(msgTitle, buffer) { }
async polyfillResponse(msgTitle, buffer) { }
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 deleted = Buffer.allocUnsafe(deleteCount);
buf.copy(deleted, 0, offset, end);
const moveLength = buf.length - end;
if (moveLength > 0) {
buf.copy(buf, offset, end, buf.length);
}
buf.fill(0, buf.length - deleteCount);
this.shrinkCount += deleteCount;
return deleted;
const newBuf = Buffer.concat([
buf.slice(0, offset),
buf.slice(end)
]);
return newBuf;
}
insert(buf, offset, insertBuf) {
const availableSpace = buf.length - offset;
const insertLength = Math.min(insertBuf.length, availableSpace);
buf.copy(buf, offset + insertLength, offset, buf.length - insertLength);
insertBuf.copy(buf, offset, 0, insertLength);
this.shrinkCount -= insertLength;
return buf;
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 {
shrinkCount = 0;
async polyfillGameMsg(msgTitle: string, buffer: Buffer) {}
async polyfillGameMsg(msgTitle: string, buffer: Buffer): Promise<Buffer | undefined> {
return;
}
async polyfillResponse(msgTitle: string, buffer: Buffer) {}
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);
......@@ -11,31 +14,24 @@ export class BasePolyfiller {
deleteCount = Math.min(deleteCount, buf.length - offset);
const end = offset + deleteCount;
const deleted = Buffer.allocUnsafe(deleteCount);
buf.copy(deleted, 0, offset, end);
const newBuf = Buffer.concat([
buf.slice(0, offset),
buf.slice(end)
]);
const moveLength = buf.length - end;
if (moveLength > 0) {
buf.copy(buf, offset, end, buf.length);
}
buf.fill(0, buf.length - deleteCount);
this.shrinkCount += deleteCount;
return deleted;
return newBuf;
}
insert(buf: Buffer, offset: number, insertBuf: Buffer) {
const availableSpace = buf.length - offset;
const insertLength = Math.min(insertBuf.length, availableSpace);
insert(buf: Buffer, offset: number, insertBuf: Buffer): Buffer {
if (offset < 0) offset = 0;
if (offset > buf.length) offset = buf.length;
buf.copy(buf, offset + insertLength, offset, buf.length - insertLength);
insertBuf.copy(buf, offset, 0, insertLength);
this.shrinkCount -= insertLength;
const newBuf = Buffer.concat([
buf.slice(0, offset),
insertBuf,
buf.slice(offset)
]);
return buf;
return newBuf;
}
}
......@@ -15,17 +15,15 @@ const getPolyfillers = (version) => {
};
async function polyfillGameMsg(version, msgTitle, buffer) {
const polyfillers = getPolyfillers(version);
let shrinkCount = 0;
let mutated = false;
for (const polyfiller of polyfillers) {
await polyfiller.polyfillGameMsg(msgTitle, buffer);
if (polyfiller.shrinkCount > 0) {
if (polyfiller.shrinkCount === 0x3f3f3f3f) {
return 0x3f3f3f3f; // special case for cancel message
}
shrinkCount += polyfiller.shrinkCount;
const newBuf = await polyfiller.polyfillGameMsg(msgTitle, buffer);
if (newBuf) {
mutated = true;
buffer = newBuf;
}
}
return shrinkCount;
return mutated ? buffer : undefined;
}
async function polyfillResponse(version, msgTitle, buffer) {
const polyfillers = getPolyfillers(version);
......
......@@ -15,17 +15,15 @@ const getPolyfillers = (version: number) => {
export async function polyfillGameMsg(version: number, msgTitle: string, buffer: Buffer) {
const polyfillers = getPolyfillers(version);
let shrinkCount = 0;
let mutated = false;
for (const polyfiller of polyfillers) {
await polyfiller.polyfillGameMsg(msgTitle, buffer);
if (polyfiller.shrinkCount > 0) {
if (polyfiller.shrinkCount === 0x3f3f3f3f) {
return 0x3f3f3f3f; // special case for cancel message
}
shrinkCount += polyfiller.shrinkCount;
const newBuf = await polyfiller.polyfillGameMsg(msgTitle, buffer);
if (newBuf) {
mutated = true;
buffer = newBuf;
}
}
return shrinkCount;
return mutated ? buffer : undefined;
}
export async function polyfillResponse(version: number, msgTitle: string, buffer: Buffer) {
......
......@@ -17,7 +17,7 @@ class Polyfiller1361 extends base_polyfiller_1.BasePolyfiller {
// buf[0]: MSG_CONFIRM_CARDS
// buf[1]: playerid
// buf[2]: ADDED skip_panel
this.splice(buffer, 2, 1);
return this.splice(buffer, 2, 1);
}
else if (msgTitle === 'SELECT_CHAIN') {
// buf[0]: MSG_SELECT_CHAIN
......@@ -46,10 +46,10 @@ class Polyfiller1361 extends base_polyfiller_1.BasePolyfiller {
// 从后往前 splice 每个 item 的 forced 字段
for (let i = size - 1; i >= 0; i--) {
const itemOffset = itemStartOffset + i * 14;
this.splice(buffer, itemOffset + 1, 1); // 删除每个 item 的 forced(第 1 字节)
buffer = this.splice(buffer, itemOffset + 1, 1); // 删除每个 item 的 forced(第 1 字节)
}
// 最后再插入旧版所需的 forced 标志
this.insert(buffer, 4, Buffer.from([anyForced ? 1 : 0]));
return this.insert(buffer, 4, Buffer.from([anyForced ? 1 : 0]));
}
else if (msgTitle === 'SELECT_SUM') {
// buf[0]: MSG_SELECT_SUM
......
......@@ -15,7 +15,7 @@ export class Polyfiller1361 extends BasePolyfiller {
// buf[0]: MSG_CONFIRM_CARDS
// buf[1]: playerid
// buf[2]: ADDED skip_panel
this.splice(buffer, 2, 1);
return this.splice(buffer, 2, 1);
} else if (msgTitle === 'SELECT_CHAIN') {
// buf[0]: MSG_SELECT_CHAIN
// buf[1]: playerid
......@@ -46,11 +46,11 @@ export class Polyfiller1361 extends BasePolyfiller {
// 从后往前 splice 每个 item 的 forced 字段
for (let i = size - 1; i >= 0; i--) {
const itemOffset = itemStartOffset + i * 14;
this.splice(buffer, itemOffset + 1, 1); // 删除每个 item 的 forced(第 1 字节)
buffer = this.splice(buffer, itemOffset + 1, 1); // 删除每个 item 的 forced(第 1 字节)
}
// 最后再插入旧版所需的 forced 标志
this.insert(buffer, 4, Buffer.from([anyForced ? 1 : 0]));
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
......
......@@ -2594,11 +2594,11 @@ 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]
shrink_count = await msg_polyfill.polyfillGameMsg(client.actual_version, msg_name, buffer)
if shrink_count == 0x3f3f3f3f
return true
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.from(buffer.slice(0, buffer.length - shrink_count))
client.last_game_msg = new_buf or buffer
client.last_game_msg_title = msg_name
#console.log client.pos, "MSG", msg_name
if msg_name == 'RETRY' and room.recovering
......@@ -2873,8 +2873,8 @@ ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
room.recover_buffers[client.pos].push(buffer)
return true
if shrink_count > 0
return "_shrink_#{shrink_count}"
if new_buf
return buffer
else
return false
......
......@@ -3405,19 +3405,19 @@
};
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, record_last_game_msg, ref, ref1, ref2, ref3, ref4, ref5, room, shrink_count, 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];
shrink_count = (await msg_polyfill.polyfillGameMsg(client.actual_version, msg_name, buffer));
if (shrink_count === 0x3f3f3f3f) {
return true;
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.from(buffer.slice(0, buffer.length - shrink_count));
client.last_game_msg = new_buf || buffer;
return client.last_game_msg_title = msg_name;
};
//console.log client.pos, "MSG", msg_name
......@@ -3792,8 +3792,8 @@
}
return true;
}
if (shrink_count > 0) {
return `_shrink_${shrink_count}`;
if (new_buf) {
return buffer;
} else {
return false;
}
......
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