@perion/net
Version:
A few networking utilities for MapleStory server development
126 lines • 3.76 kB
JavaScript
const Writer = require('../writer.js');
const Params = require('../protocol/params.js');
const path = require('path');
class Protocol {
constructor(protoPaths) {
this.protocols = {};
this.available = [];
for (const protoPath of protoPaths) {
try {
const protocol = require(path.resolve(protoPath));
this.available.push(protocol.name);
this.protocols[protocol.name] = protocol.messages;
} catch (err) {
throw new Error(`Could not load protocol file: ${protoPath}`);
}
}
}
static _parseKey(protocolKey) {
const split = protocolKey.split('.');
const protoName = split[0];
const msgName = split[1];
return {protoName, msgName};
}
findSchema(protoName, opcode) {
const protocol = this.protocols[protoName];
for (const msg of Object.values(protocol)) {
if (opcode === Number(msg.opcode)) return msg.schema;
}
return null;
}
schema(protocolKey) {
const {protoName, msgName} = Protocol._parseKey(protocolKey);
const protocol = this.protocols[protoName];
const message = protocol[msgName];
if (!(msgName in protocol)) {
throw new Error(`Unknown message name ${protocolKey}`);
}
if (!('schema' in message)) {
throw new Error(`Missing schema ${protocolKey}.schema`);
}
return message.schema;
}
eval(schema, parser) {
for (const typeStr of schema) {
const type = typeStr.split('?')[0];
const params = Params.parse(typeStr);
if (Object.keys(params).length === 0) {
parser[type]();
} else parser[type](params);
}
}
build(protocolKey, values) {
const {protoName, msgName} = Protocol._parseKey(protocolKey);
const protocol = this.protocols[protoName];
// TODO: Check for protocol.messages
if (!protocol) {
throw new Error(
`Tried to build from unknown protocol ${protoName}.${msgName}`
);
}
let length = 0;
const message = protocol[msgName];
if (!(msgName in protocol)) {
throw new Error(`Unknown message name ${protocolKey}`);
}
const schema = message.schema;
const opcode = message.opcode;
if (!('schema' in message)) {
throw new Error(`Missing schema ${protocolKey}.schema`);
}
if(!('opcode' in message)) {
throw new Error(`Missing opcode ${protocolKey}.opcode`);
}
if (message.schema.length !== values.length) {
throw new Error('Message schema not matching input values');
}
// Calculate packet length
for (const idx in schema) {
const type = schema[idx].split('?')[0];
const params = Params.parse(schema[idx]);
switch (type) {
case 'byte':
case 'ubyte':
case 'bool':
length += 1;
break;
case 'short':
case 'ushort':
length += 2;
break;
case 'int':
case 'uint':
case 'pos':
length += 4;
break;
case 'long':
case 'ulong':
length += 8;
break;
case 'ascii':
length += Number(params.length);
break;
case 'nullascii':
length += values[idx].length + 1;
break;
case 'mapleascii':
length += values[idx].length + 2;
break;
default:
throw new Error(`Unknown datatype ${type}`);
}
}
const writer = new Writer(2 + length);
writer.short(Number(opcode));
// Map the schema to values
for (const idx in schema) {
const type = schema[idx].split('?')[0];
const params = Params.parse(type);
if (Object.keys(params).length === 0) {
writer[type](values[idx]);
}
}
return writer.buffer();
}
}
module.exports = Protocol;