Skip to content

Commit

Permalink
Merge pull request #138 from rune-js/buffer-refactor
Browse files Browse the repository at this point in the history
Buffer refactor
  • Loading branch information
TheBlackParade authored Mar 23, 2020
2 parents 8641fc4 + 2876d5c commit 2eae9c5
Show file tree
Hide file tree
Showing 42 changed files with 526 additions and 950 deletions.
16 changes: 13 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"license": "GPL-3.0",
"dependencies": {
"@hapi/joi": "^16.1.8",
"@runejs/cache-parser": "0.5.0",
"@runejs/cache-parser": "0.6.3",
"@runejs/byte-buffer": "1.0.8",
"@runejs/logger": "^1.0.0",
"bigi": "^1.4.2",
"body-parser": "^1.19.0",
Expand Down
20 changes: 10 additions & 10 deletions src/game-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import * as net from 'net';
import { watch } from 'chokidar';
import * as CRC32 from 'crc-32';

import { RsBuffer } from './net/rs-buffer';
import { World } from './world/world';
import { ClientConnection } from './net/client-connection';
import { logger } from '@runejs/logger';
import { Cache } from '@runejs/cache-parser';
import { parseServerConfig, ServerConfig } from '@server/world/config/server-config';
import { ByteBuffer } from '@runejs/byte-buffer';

import { loadPlugins } from '@server/plugins/plugin-loader';
import { ActionPlugin, ActionType, sort } from '@server/plugins/plugin';
Expand All @@ -30,7 +30,7 @@ import { setQuestPlugins } from '@server/world/config/quests';
export let serverConfig: ServerConfig;
export let cache: Cache;
export let world: World;
export let crcTable: Buffer;
export let crcTable: ByteBuffer;

export async function injectPlugins(): Promise<void> {
const actionTypes: { [key: string]: ActionPlugin[] } = {};
Expand Down Expand Up @@ -63,16 +63,16 @@ export async function injectPlugins(): Promise<void> {

function generateCrcTable(): void {
const index = cache.metaChannel;
const indexLength = index.getBuffer().length;
const buffer = RsBuffer.create(4048);
buffer.writeByte(0);
buffer.writeIntBE(indexLength);
const indexLength = index.length;
const buffer = new ByteBuffer(4048);
buffer.put(0, 'BYTE');
buffer.put(indexLength, 'INT');
for(let file = 0; file < (indexLength / 6); file++) {
const crcValue = CRC32.buf(cache.getRawFile(255, file).getBuffer());
buffer.writeIntBE(crcValue);
const crcValue = CRC32.buf(cache.getRawFile(255, file));
buffer.put(crcValue, 'INT');
}

crcTable = buffer.getBuffer();
crcTable = buffer;
}

export function runGameServer(): void {
Expand Down Expand Up @@ -107,7 +107,7 @@ export function runGameServer(): void {

socket.on('data', data => {
if(clientConnection) {
clientConnection.parseIncomingData(new RsBuffer(data));
clientConnection.parseIncomingData(new ByteBuffer(data));
}
});

Expand Down
6 changes: 3 additions & 3 deletions src/net/client-connection.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Socket } from 'net';
import { Player } from '@server/world/actor/player/player';
import { world } from '@server/game-server';
import { RsBuffer } from './rs-buffer';
import { LoginHandshakeParser } from './data-parser/login-handshake-parser';
import { ClientLoginParser } from './data-parser/client-login-parser';
import { ClientPacketDataParser } from './data-parser/client-packet-data-parser';
import { DataParser } from './data-parser/data-parser';
import { VersionHandshakeParser } from '@server/net/data-parser/version-handshake-parser';
import { UpdateServerParser } from '@server/net/data-parser/update-server-parser';
import { ByteBuffer } from '@runejs/byte-buffer';

enum ConnectionStage {
VERSION_HANDSHAKE = 'VERSION_HANDSHAKE',
Expand Down Expand Up @@ -35,10 +35,10 @@ export class ClientConnection {
this.dataParser = null;
}

public parseIncomingData(buffer?: RsBuffer): void {
public parseIncomingData(buffer?: ByteBuffer): void {
try {
if(!this.connectionStage) {
const packetId = buffer.readUnsignedByte();
const packetId = buffer.get('BYTE', 'UNSIGNED');

if(packetId === 15) {
this.connectionStage = ConnectionStage.VERSION_HANDSHAKE;
Expand Down
48 changes: 24 additions & 24 deletions src/net/data-parser/client-login-parser.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import BigInteger from 'bigi';
import { RsBuffer } from '@server/net/rs-buffer';
import { Player } from '@server/world/actor/player/player';
import { Isaac } from '@server/net/isaac';
import { serverConfig, world } from '@server/game-server';
import { DataParser } from './data-parser';
import { logger } from '@runejs/logger/dist/logger';
import { ByteBuffer } from '@runejs/byte-buffer';

const VALID_CHARS = ['_', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
Expand Down Expand Up @@ -32,61 +32,61 @@ export class ClientLoginParser extends DataParser {
private readonly rsaModulus = BigInteger(serverConfig.rsaMod);
private readonly rsaExponent = BigInteger(serverConfig.rsaExp);

public parse(buffer?: RsBuffer): void {
public parse(buffer?: ByteBuffer): void {
if(!buffer) {
throw new Error('No data supplied for login');
}

const loginType = buffer.readUnsignedByte();
const loginType = buffer.get('BYTE', 'UNSIGNED');

if(loginType !== 16 && loginType !== 18) {
throw new Error('Invalid login type ' + loginType);
}

let loginEncryptedSize = buffer.readUnsignedByte() - (36 + 1 + 1 + 2);
let loginEncryptedSize = buffer.get('BYTE', 'UNSIGNED') - (36 + 1 + 1 + 2);

if(loginEncryptedSize <= 0) {
throw new Error('Invalid login packet length ' + loginEncryptedSize);
}

const gameVersion = buffer.readIntBE();
const gameVersion = buffer.get('INT');

if(gameVersion !== 435) {
throw new Error('Invalid game version ' + gameVersion);
}

const isLowDetail: boolean = buffer.readByte() === 1;
const isLowDetail: boolean = buffer.get('BYTE') === 1;

for(let i = 0; i < 13; i++) {
buffer.readIntBE(); // Cache indices
buffer.get('INT'); // Cache indices
}

loginEncryptedSize--;

const rsaBytes = buffer.readUnsignedByte();
const rsaBytes = buffer.get('BYTE', 'UNSIGNED');

const encryptedBytes: Buffer = Buffer.alloc(rsaBytes);
buffer.getBuffer().copy(encryptedBytes, 0, buffer.getReaderIndex());
const decrypted: RsBuffer = new RsBuffer(BigInteger.fromBuffer(encryptedBytes).modPow(this.rsaExponent, this.rsaModulus).toBuffer());
buffer.copy(encryptedBytes, 0, buffer.readerIndex);
const decrypted = new ByteBuffer(BigInteger.fromBuffer(encryptedBytes).modPow(this.rsaExponent, this.rsaModulus).toBuffer());

const blockId = decrypted.readByte();
const blockId = decrypted.get('BYTE');

if(blockId !== 10) {
throw new Error('Invalid block id ' + blockId);
}

const clientKey1 = decrypted.readIntBE();
const clientKey2 = decrypted.readIntBE();
const incomingServerKey = decrypted.readLongBE();
const clientKey1 = decrypted.get('INT');
const clientKey2 = decrypted.get('INT');
const incomingServerKey = BigInt(decrypted.get('LONG'));

if(this.clientConnection.serverKey !== incomingServerKey) {
throw new Error(`Server key mismatch - ${this.clientConnection.serverKey} != ${incomingServerKey}`);
}

const clientUuid = decrypted.readIntBE();
const usernameLong = decrypted.readLongBE();
const clientUuid = decrypted.get('INT');
const usernameLong = BigInt(decrypted.get('LONG'));
const username = longToName(usernameLong);
const password = decrypted.readNewString();
const password = decrypted.getString();

logger.info(`Login request: ${username}/${password}`);

Expand All @@ -106,13 +106,13 @@ export class ClientLoginParser extends DataParser {

world.registerPlayer(player);

const outputBuffer = RsBuffer.create();
outputBuffer.writeByte(2); // login response code
outputBuffer.writeByte(player.rights.valueOf());
outputBuffer.writeByte(0); // ???
outputBuffer.writeShortBE(player.worldIndex + 1);
outputBuffer.writeByte(0); // ???
this.clientConnection.socket.write(outputBuffer.getData());
const outputBuffer = new ByteBuffer(6);
outputBuffer.put(2, 'BYTE'); // login response code
outputBuffer.put(player.rights.valueOf(), 'BYTE');
outputBuffer.put(0, 'BYTE'); // ???
outputBuffer.put(player.worldIndex + 1, 'SHORT');
outputBuffer.put(0, 'BYTE'); // ???
this.clientConnection.socket.write(outputBuffer);

player.init();

Expand Down
37 changes: 21 additions & 16 deletions src/net/data-parser/client-packet-data-parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RsBuffer } from '@server/net/rs-buffer';
import { incomingPacketSizes } from '@server/net/incoming-packet-sizes';
import { handlePacket } from '@server/net/incoming-packet-directory';
import { DataParser } from './data-parser';
import { ByteBuffer } from '@runejs/byte-buffer';

/**
* Parses incoming packet data from the game client once the user is fully authenticated.
Expand All @@ -10,16 +10,16 @@ export class ClientPacketDataParser extends DataParser {

private activePacketId: number = null;
private activePacketSize: number = null;
private activeBuffer: RsBuffer;
private activeBuffer: ByteBuffer;

public parse(buffer?: RsBuffer): void {
public parse(buffer?: ByteBuffer): void {
if(!this.activeBuffer) {
this.activeBuffer = buffer;
} else if(buffer) {
const newBuffer = new RsBuffer(this.activeBuffer.getUnreadData());
const activeLength = newBuffer.getBuffer().length;
newBuffer.ensureCapacity(activeLength + buffer.getBuffer().length);
buffer.getBuffer().copy(newBuffer.getBuffer(), activeLength, 0);
const readable = this.activeBuffer.readable;
const newBuffer = new ByteBuffer(readable + buffer.length);
this.activeBuffer.copy(newBuffer, 0, this.activeBuffer.readerIndex);
buffer.copy(newBuffer, readable, 0);
this.activeBuffer = newBuffer;
}

Expand All @@ -34,41 +34,46 @@ export class ClientPacketDataParser extends DataParser {
const inCipher = this.clientConnection.player.inCipher;

if(this.activePacketId === -1) {
if(this.activeBuffer.getReadable() < 1) {
if(this.activeBuffer.readable < 1) {
return;
}

this.activePacketId = this.activeBuffer.readByte() & 0xff;
this.activePacketId = this.activeBuffer.get('BYTE', 'UNSIGNED');
this.activePacketId = (this.activePacketId - inCipher.rand()) & 0xff;
this.activePacketSize = incomingPacketSizes[this.activePacketId];
}

// Packet will provide the size
if(this.activePacketSize === -1) {
if(this.activeBuffer.getReadable() < 1) {
if(this.activeBuffer.readable < 1) {
return;
}

this.activePacketSize = this.activeBuffer.readUnsignedByte();
this.activePacketSize = this.activeBuffer.get('BYTE', 'UNSIGNED');
}

// Packet has no set size
let clearBuffer = false;
if(this.activePacketSize === -3) {
if(this.activeBuffer.getReadable() < 1) {
if(this.activeBuffer.readable < 1) {
return;
}

this.activePacketSize = this.activeBuffer.getReadable();
this.activePacketSize = this.activeBuffer.readable;
clearBuffer = true;
}

if(this.activeBuffer.getReadable() < this.activePacketSize) {
if(this.activeBuffer.readable < this.activePacketSize) {
return;
}

// read packet data
const packetData = this.activePacketSize !== 0 ? this.activeBuffer.readBytes(this.activePacketSize) : null;
let packetData = null;
if(this.activePacketSize !== 0) {
packetData = new ByteBuffer(this.activePacketSize);
this.activeBuffer.copy(packetData, 0, this.activeBuffer.readerIndex, this.activeBuffer.readerIndex + this.activePacketSize);
this.activeBuffer.readerIndex += this.activePacketSize;
}
handlePacket(this.clientConnection.player, this.activePacketId, this.activePacketSize, packetData);

if(clearBuffer) {
Expand All @@ -78,7 +83,7 @@ export class ClientPacketDataParser extends DataParser {
this.activePacketId = null;
this.activePacketSize = null;

if(this.activeBuffer !== null && this.activeBuffer.getReadable() > 0) {
if(this.activeBuffer !== null && this.activeBuffer.readable > 0) {
this.parse();
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/net/data-parser/data-parser.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ClientConnection } from '@server/net/client-connection';
import { RsBuffer } from '@server/net/rs-buffer';
import { ByteBuffer } from '@runejs/byte-buffer';

export abstract class DataParser {

public constructor(protected readonly clientConnection: ClientConnection) {
}

public abstract parse(buffer?: RsBuffer, packetId?: number): void | boolean;
public abstract parse(buffer?: ByteBuffer, packetId?: number): void | boolean;

}
14 changes: 7 additions & 7 deletions src/net/data-parser/login-handshake-parser.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { RsBuffer } from '@server/net/rs-buffer';
import { DataParser } from './data-parser';
import { ByteBuffer } from '@runejs/byte-buffer';

/**
* Controls the initial login handshake with the server.
*/
export class LoginHandshakeParser extends DataParser {

public parse(buffer: RsBuffer, packetId: number): void {
public parse(buffer: ByteBuffer, packetId: number): void {
if(!buffer) {
throw new Error('No data supplied for login handshake');
}

if(packetId === 14) {
buffer.readUnsignedByte(); // Name hash
buffer.get('BYTE', 'UNSIGNED'); // Name hash

const serverKey = BigInt(13371337); // TODO generate server_key

const outputBuffer = RsBuffer.create();
outputBuffer.writeByte(0); // Initial server login response -> 0 for OK
outputBuffer.writeLongBE(serverKey);
this.clientConnection.socket.write(outputBuffer.getData());
const outputBuffer = new ByteBuffer(9);
outputBuffer.put(0, 'BYTE'); // Initial server login response -> 0 for OK
outputBuffer.put(serverKey, 'LONG');
this.clientConnection.socket.write(outputBuffer);

this.clientConnection.serverKey = serverKey;
} else {
Expand Down
Loading

0 comments on commit 2eae9c5

Please sign in to comment.