Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffer refactor #138

Merged
merged 3 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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