Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
Expand Down
2 changes: 1 addition & 1 deletion libs/@rustymotors/binary/src/lib/Bytable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class Bytable extends BytableBase implements BytableObject {
}
}

export function validateBuffer(buf: DataView<ArrayBufferLike> | ArrayBufferLike, direction: string) {
export function validateBuffer(buf: DataView<ArrayBufferLike> | ArrayBufferLike | Buffer, direction: string) {
if (typeof buf === 'undefined') {
throw new Error(`Cannot ${direction} undefined buffer`);
}
Expand Down
4 changes: 3 additions & 1 deletion libs/@rustymotors/binary/src/lib/BytableHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export class BytableHeader extends Bytable {
return this.messageId_;
}

/** @deprecated Use `id` instead. */
/**
* @deprecated Use `id` instead. // TODO: Remove depreciated method
* */
get messageId() {
return this.id;
}
Expand Down
12 changes: 9 additions & 3 deletions libs/@rustymotors/binary/src/lib/BytableMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import { BytableData } from './BytableData.js';
import { BytableDword } from './BytableDword.js';
import { BytableHeader } from './BytableHeader.js';
import { BytableWord } from './BytableWord.js';
import { BytableObject } from './types.js';
import { getServerLogger, RawMessage } from 'rusty-motors-shared';
import { BytableObject, IBytableMessage } from './types.js';
import {
getServerLogger,
RawMessage,
} from 'rusty-motors-shared';

export class BytableStructure extends BytableBase implements BytableObject {
protected fields_: Array<BytableObject> = [];
Expand Down Expand Up @@ -152,7 +155,8 @@ export const BytableFieldTypes = {
Buffer: BytableBuffer,
CString: BytableCString,
};
export class BytableMessage extends Bytable {

export class BytableMessage extends Bytable implements IBytableMessage {
protected header_: BytableHeader = new BytableHeader();
protected fields_: Array<BytableObject> = [];
protected serializeOrder_: Array<{
Expand Down Expand Up @@ -428,3 +432,5 @@ export function createGameMessage(buffer?: Buffer) {

return message;
}


2 changes: 1 addition & 1 deletion libs/@rustymotors/binary/src/lib/binary.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BytableMessage } from "./BytableMessage";
import { BytableMessage } from "./BytableMessage.js";
import { GamePacket } from "rusty-motors-shared-packets";

export function binary(): string {
Expand Down
32 changes: 32 additions & 0 deletions libs/@rustymotors/binary/src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { BytableHeader } from './BytableHeader.js';
import { BytableFieldTypes } from './BytableMessage.js';

/**
* A field that can be serialized and deserialized
*/
Expand Down Expand Up @@ -32,3 +35,32 @@ export interface BytableObject {
setName(name: string): void;
setValue(value: string | number | Buffer): void;
}
export interface IBytableMessage {
deserialize(buffer: Buffer): void;
get header(): BytableHeader;
get serializeSize(): number;
serialize(): Buffer<ArrayBuffer>;
get json(): { name: string; serializeSize: number; header: { name: string; id: number; len: number; version: 0 | 1; serializeSize: number; }; fields: Record<string, unknown>[]; };
setName(name: string): void;
toString(): string;
setSerializeOrder(serializeOrder: Array<{
name: string;
field: keyof typeof BytableFieldTypes;
}>): void;
getField(name: string): BytableObject | undefined;
getFieldValueByName(name: string): string | number | Buffer<ArrayBufferLike> | undefined;
htonl(value: number): Buffer<ArrayBuffer>;
coerceValue(value: string | number | Buffer): Buffer<ArrayBufferLike>;
setFieldValueByName(name: string, value: string | number | Buffer): void;
setVersion(version: 0 | 1): void;
getBody(): Buffer<ArrayBuffer>;
setBody(buffer: Buffer): void;
get data(): Buffer<ArrayBufferLike>;
set data(buffer: Buffer<ArrayBufferLike>);
toHexString(): string;
_doDeserialize(buf: Buffer): void;
_doSerialize(): Buffer<ArrayBuffer>;
get name(): string;
get value(): string | number | Buffer<ArrayBufferLike>;
setValue(value: string | number | Buffer): void;
}
43 changes: 43 additions & 0 deletions libs/@rustymotors/network/src/network.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, it, expect } from 'vitest';
import { ClientConnection, newConnection } from './network.js';
import { createMockTcpSocket, createMockUdpSocket } from 'rusty-motors-shared';

describe('ClientConnection', () => {
it('should initialize with TCP socket', () => {
const socket = createMockTcpSocket();

const connection = new ClientConnection(socket, 'TCP');
expect(connection).toBeInstanceOf(ClientConnection);
expect(connection.protocol).toBe('TCP');
expect(connection.socket).toBe(socket);
expect(connection.encryptionSetup).toBe(false);
expect(connection.encryption).toBeNull();
});

it('should initialize with UDP socket', () => {
const socket = createMockUdpSocket();

const connection = new ClientConnection(socket, 'UDP');
expect(connection).toBeInstanceOf(ClientConnection);
expect(connection.protocol).toBe('UDP');
expect(connection.socket).toBe(socket);
});
});

describe('newConnection', () => {
it('should create ClientConnection for TCP socket', () => {
const socket = createMockTcpSocket();

const connection = newConnection(socket);
expect(connection).toBeInstanceOf(ClientConnection);
expect(connection.protocol).toBe('TCP');
});

it('should create ClientConnection for UDP socket', () => {
const socket = createMockUdpSocket();

const connection = newConnection(socket);
expect(connection).toBeInstanceOf(ClientConnection);
expect(connection.protocol).toBe('UDP');
});
});
55 changes: 29 additions & 26 deletions libs/@rustymotors/network/src/network.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,59 @@
import { Socket as SocketUDP, type RemoteInfo } from 'node:dgram';
import { Socket as SocketUDP } from 'node:dgram';
import EventEmitter from 'node:events';
import { Socket as SocketTCP, type AddressInfo } from 'node:net';
import type { IClientConnection } from './types.js';
import { Socket as SocketTCP } from 'node:net';
import type { Cipheriv, Decipheriv } from 'crypto';


export class ClientConnection
extends EventEmitter
implements IClientConnection
{
personaName: string = '';
personaId: number = 0;
ip: string;
port: number;
protocol: 'TCP' | 'UDP';
socket: SocketTCP | SocketUDP;
encryptionSetup: false;
encryptionSetup: false = false;
encryption:
| ({ cmdEnc: Cipheriv; cmdDec: Decipheriv } & {
cmdEnc: Cipheriv;
cmdDec: Decipheriv;
})
| null;
| null = null;

constructor(socket: SocketTCP | SocketUDP) {
constructor(socket: SocketTCP | SocketUDP, protocol: 'TCP' | 'UDP') {
super();
const remoteAddress = socket.remoteAddress;
const localPort = (socket.address() as AddressInfo).port;
this.socket = socket;
this.protocol = protocol;

if (
typeof remoteAddress === 'undefined' ||
typeof localPort === 'undefined'
) {
throw new Error('both remoteAddress and localPort must be defined');
if (protocol === 'TCP' && socket instanceof SocketTCP) {
this.ip = socket.remoteAddress || '';
this.port = socket.localPort || 0;
} else if (protocol === 'UDP' && socket instanceof SocketUDP) {
// UDP sockets don't have a single remote address/port in the same way,
// but for a connected UDP socket or just initialization we might need to handle it differently.
// For now, initializing with defaults or extracting from address() if possible.
try {
const address = socket.address();
this.ip = address.address;
this.port = address.port;
} catch (e) {
// Socket might not be bound yet
this.ip = '';
this.port = 0;
}
} else {
this.ip = '';
this.port = 0;
}
}
}

export function newConnection(socket: SocketTCP | SocketUDP): ClientConnection;

export function newConnection(socket: SocketTCP | SocketUDP): ClientConnection {
if (socket instanceof SocketTCP) {
const connection: ClientConnection = {
personaName: '',
personaId: 0,
ip: remoteAddress,
port: localPort,
protocol: 'TCP',
socket: socket,
encryptionSetup: false,
encryption: null,
};
return new ClientConnection(socket, 'TCP');
} else if (socket instanceof SocketUDP) {
return new ClientConnection(socket, 'UDP');
} else {
throw new Error('socket is not either TCP or UDP');
}
Expand Down
8 changes: 4 additions & 4 deletions libs/@rustymotors/protocol/src/MCOProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class MCOProtocol {
socket: Socket;
log?: ServerLogger;
}) {
log.debug(
log.verbose(
"Accepting incoming socket",
{ connectionId, port },
);
Expand All @@ -71,7 +71,7 @@ class MCOProtocol {
});
socket.on("error", (error) => {
if (error.message === "read ECONNRESET") {
log.debug(
log.verbose(
"Connection reset by client",
{ connectionId },
);
Expand All @@ -94,7 +94,7 @@ class MCOProtocol {
log?: ServerLogger;
}) {
const incomingPacket = createRawMessage(data);
log.debug(
log.verbose(
`Received packet: ${incomingPacket.serialize().toString("hex")}`,
{ connectionId },
);
Expand All @@ -118,7 +118,7 @@ class MCOProtocol {
);
}

log.debug(
log.verbose(
"Packet processed",
{ connectionId },
);
Expand Down
2 changes: 1 addition & 1 deletion libs/@rustymotors/protocol/src/defaultMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function defaultMessageHandler({
message: BytableMessage | null;
}> {
const messageId = message.header.messageId;
log.debug(
log.verbose(
"Not yet implemented",
{ connectionId, messageId: messageId.toString(16) },
);
Expand Down
14 changes: 7 additions & 7 deletions libs/@rustymotors/protocol/src/serverLoginMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export async function serverLoginMessageHandler({
]);
inboundMessage.deserialize(message.serialize());
const messageId = inboundMessage.header.messageId;
log.debug(
log.verbose(
"Processing server login message",
{ connectionId, messageId: messageId.toString(16) },
);
Expand Down Expand Up @@ -94,7 +94,7 @@ export async function serverLoginMessageHandler({
decrypted: sessionKey,
},
); // 12 bytes
log.fatal(
log.error(
`Error decrypting session key: ${(error as Error).message}`,
{
connectionId,
Expand All @@ -111,14 +111,14 @@ export async function serverLoginMessageHandler({
throw Error("No session key found");
}

log.debug(
log.verbose(
"Session key decrypted",
{
connectionId,
},
);

log.debug(
log.verbose(
"Creating outbound message",
{
connectionId,
Expand All @@ -128,7 +128,7 @@ export async function serverLoginMessageHandler({
const responseCode = 0x601;

const loginResponseMessage = createRawMessage();
loginResponseMessage.header.setMessageId(responseCode);
loginResponseMessage.header.setId(responseCode);
loginResponseMessage.setSerializeOrder([
{ name: "ban", field: "Dword" },
{ name: "gag", field: "Dword" },
Expand Down Expand Up @@ -156,10 +156,10 @@ export async function serverLoginMessageHandler({
const body = serialize(fields);

const responseMessage = createRawMessage();
responseMessage.header.setMessageId(responseCode);
responseMessage.header.setId(responseCode);
responseMessage.setBody(body);

log.debug(
log.verbose(
"Outbound message created",
{
connectionId,
Expand Down
2 changes: 1 addition & 1 deletion libs/@rustymotors/protocol/src/writePacket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function writePacket({
data: Buffer;
log?: ServerLogger;
}) {
log.debug(
log.verbose(
`Writing packet: ${data.toString("hex")}`,
{ connectionId },
);
Expand Down
3 changes: 0 additions & 3 deletions packages/cli/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ describe("ConsoleThread", () => {
info: () => vi.fn(),
error: () => vi.fn(),
warn: () => vi.fn(),
debug: () => vi.fn(),
trace: () => vi.fn(),
fatal: () => vi.fn(),
} as any;
const instance = new ConsoleThread({ parentThread, log });
expect(instance).toBeInstanceOf(ConsoleThread);
Expand Down
Loading
Loading