Skip to content

Commit

Permalink
Merge pull request #131 from rune-js/update-server
Browse files Browse the repository at this point in the history
Implementing the full cache update server.
  • Loading branch information
TheBlackParade authored Mar 16, 2020
2 parents 2669f8d + dbf174c commit bd73d78
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 48 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ Currently the server is set up for the 435 revision of the game, which was a gam
- NPC spawn loading via YAML configuration.
- Player & NPC pathing validation via collision and tile maps generated from the 377 game cache.
- A basic REST service for polling logged in users.
- Full functional update server.
- A diverse TypeScript plugin system for easily writing new content based off of in-game actions.
- Flexible quest and dialogue systems for easy content development.

## Usage

Expand Down Expand Up @@ -69,4 +71,4 @@ RuneJS supports the 435 RuneScape game client being renamed by [Promises](https:

#### Update Server

RuneJS does not provide a fully working update server for revision 435. While this is planned for a future release, the [refactored-client-435](https://github.com/Promises/refactored-client-435) currently has the update server disabled. Due to this, we can only recommend this client at this time.
RuneJS provides a fully working update server for the 435 client to use. The update server runs alongside the regular game server using the same port, so no additional configuration is required. Simply start the server and then your game client.
31 changes: 28 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,10 +24,11 @@
"license": "GPL-3.0",
"dependencies": {
"@hapi/joi": "^16.1.8",
"@runejs/cache-parser": "0.3.0",
"@runejs/cache-parser": "0.4.5",
"@runejs/logger": "^1.0.0",
"bigi": "^1.4.2",
"body-parser": "^1.19.0",
"crc-32": "^1.2.0",
"express": "^4.17.1",
"js-yaml": "^3.13.1",
"lodash": "^4.17.15",
Expand Down
25 changes: 24 additions & 1 deletion src/game-server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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';
Expand All @@ -25,10 +26,12 @@ import { setPlayerInitPlugins } from '@server/world/actor/player/player';
import { setNpcInitPlugins } from '@server/world/actor/npc/npc';
import { setQuestPlugins } from '@server/world/config/quests';


export let serverConfig: ServerConfig;
export let gameCache377: EarlyFormatGameCache;
export let gameCache: NewFormatGameCache;
export let world: World;
export let crcTable: Buffer;

export async function injectPlugins(): Promise<void> {
const actionTypes: { [key: string]: ActionPlugin[] } = {};
Expand Down Expand Up @@ -59,6 +62,20 @@ export async function injectPlugins(): Promise<void> {
setNpcInitPlugins(actionTypes[ActionType.NPC_INIT]);
}

function generateCrcTable(): void {
const index = gameCache.metaChannel;
const indexLength = index.getBuffer().length;
const buffer = RsBuffer.create(4048);
buffer.writeByte(0);
buffer.writeIntBE(indexLength);
for(let file = 0; file < (indexLength / 6); file++) {
const crcValue = CRC32.buf(gameCache.getRawCacheFile(255, file).getBuffer());
buffer.writeIntBE(crcValue);
}

crcTable = buffer.getBuffer();
}

export function runGameServer(): void {
serverConfig = parseServerConfig();

Expand All @@ -69,6 +86,7 @@ export function runGameServer(): void {

gameCache377 = new EarlyFormatGameCache('cache/377', { loadMaps: true, loadDefinitions: false, loadWidgets: false });
gameCache = new NewFormatGameCache('cache/435');
generateCrcTable();
world = new World();
injectPlugins().then(() => {
world.init();
Expand All @@ -87,7 +105,11 @@ export function runGameServer(): void {

net.createServer(socket => {
logger.info('Socket opened');
// socket.setNoDelay(true);

socket.setNoDelay(true);
socket.setKeepAlive(true);
socket.setTimeout(30000);

let clientConnection = new ClientConnection(socket);

socket.on('data', data => {
Expand All @@ -104,6 +126,7 @@ export function runGameServer(): void {
});

socket.on('error', error => {
logger.error(error.message);
socket.destroy();
logger.error('Socket destroyed due to connection error.');
});
Expand Down
10 changes: 5 additions & 5 deletions src/net/client-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ 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 { VersionListParser } from '@server/net/data-parser/version-list-parser';
import { UpdateServerParser } from '@server/net/data-parser/update-server-parser';

enum ConnectionStage {
VERSION_HANDSHAKE = 'VERSION_HANDSHAKE',
VERSION_LIST = 'VERSION_LIST',
UPDATE_SERVER = 'UPDATE_SERVER',
LOGIN_HANDSHAKE = 'LOGIN_HANDSHAKE',
LOGIN = 'LOGIN',
LOGGED_IN = 'LOGGED_IN'
Expand Down Expand Up @@ -54,8 +54,8 @@ export class ClientConnection {
}

if(this.connectionStage === ConnectionStage.VERSION_HANDSHAKE) {
this.connectionStage = ConnectionStage.VERSION_LIST;
this.dataParser = new VersionListParser(this);
this.connectionStage = ConnectionStage.UPDATE_SERVER;
this.dataParser = new UpdateServerParser(this);
} else if(this.connectionStage === ConnectionStage.LOGIN_HANDSHAKE) {
this.connectionStage = ConnectionStage.LOGIN;
this.dataParser = new ClientLoginParser(this);
Expand All @@ -64,9 +64,9 @@ export class ClientConnection {
this.dataParser = new ClientPacketDataParser(this);
}
} catch(err) {
this.socket.destroy();
console.error('Error decoding client data');
console.error(err);
this.socket.destroy();
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/net/data-parser/client-packet-data-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ export class ClientPacketDataParser extends DataParser {
}

if(this.activeBuffer.getReadable() < this.activePacketSize) {
//console.error('Not enough readable data for packet ' + this.activePacketId + ' with size ' + this.activePacketSize + ', but only ' +
// this.activeBuffer.getReadable() + ' data is left of ' + this.activeBuffer.getBuffer().length);
return;
}

Expand Down
84 changes: 84 additions & 0 deletions src/net/data-parser/update-server-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { RsBuffer } from '@server/net/rs-buffer';
import { DataParser } from './data-parser';
import { crcTable, gameCache } from '@server/game-server';

/**
* Handles the cache update server.
*/
export class UpdateServerParser extends DataParser {

private files: { file: number, index: number }[] = [];

public parse(buffer?: RsBuffer): void {
if(!buffer) {
return;
}

while(buffer.getReadable() >= 4) {
const type = buffer.readUnsignedByte();
const index = buffer.readUnsignedByte();
const file = buffer.readUnsignedShortBE();

switch(type) {
case 0: // queue
this.files.push({ index, file });
break;
case 1: // immediate
this.clientConnection.socket.write(this.generateFile(index, file));
break;
case 2:
case 3: // clear queue
this.files = [];
break;
case 4: // error
break;
}

while(this.files.length > 0) {
const info = this.files.shift();
this.clientConnection.socket.write(this.generateFile(info.index, info.file));
}
}
}

private generateFile(index: number, file: number): Buffer {
let cacheFile;

if(index === 255 && file === 255) {
const crcBuffer = Buffer.alloc(crcTable.length);
crcTable.copy(crcBuffer, 0, 0);
cacheFile = new RsBuffer(crcBuffer);
} else {
cacheFile = gameCache.getRawCacheFile(index, file);
}

if(!cacheFile || cacheFile.getBuffer().length === 0) {
throw `Cache file not found; file(${file}) with index(${index})`;
}

const cacheFileBuffer = cacheFile.getBuffer();

const buffer = RsBuffer.create((cacheFileBuffer.length - 2) + ((cacheFileBuffer.length - 2) / 511) + 8);
buffer.writeUnsignedByte(index);
buffer.writeUnsignedShortBE(file);

let length: number = ((cacheFileBuffer.readUInt8(1) << 24) + (cacheFileBuffer.readUInt8(2) << 16) +
(cacheFileBuffer.readUInt8(3) << 8) + cacheFileBuffer.readUInt8(4)) + 9;
if(cacheFileBuffer[0] == 0) {
length -= 4;
}

let c = 3;
for(let i = 0; i < length; i++) {
if(c == 512) {
buffer.writeUnsignedByte(255);
c = 1;
}

buffer.writeByte(cacheFileBuffer.readInt8(i));
c++;
}

return buffer.getData();
}
}
35 changes: 0 additions & 35 deletions src/net/data-parser/version-list-parser.ts

This file was deleted.

0 comments on commit bd73d78

Please sign in to comment.